-
Notifications
You must be signed in to change notification settings - Fork 95
/
best-practices.html.md.eco
119 lines (100 loc) · 4.48 KB
/
best-practices.html.md.eco
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
---
layout: 'guide'
title: 'Best practices'
---
This is a guide to the best practices to follow when creating typing files. There are a variety of different ways that typing files can be constructed. Different approaches can be used - this is intended as a guide to what approaches make sense in certain scenarios.
Also, it's a steer on how to deal with limitations in TypeScript. As much as it hurts to say it, TypeScript v1.0 is not flawless. There are certain minor flaws / shortcomings in the language which have implications for how typings are created. Here we will detail those limitations, how they can be worked around at present and how you can best vote for improvements in the language on the [TypeScript site](//typescript.codeplex.com).
### Ghost modules
Also called `non-instantiated modules`. Instead of polluting the global namespace with many interfaces, it is okay to create a module that contains *interfaces only*. This does not introduce a variable on the global namespace (see safety in below sample) and this module can only be used for *types*.
````typescript
// this pattern has 3 names in top level
interface NodeFoo { }
interface NodeBar { }
interface NodeBuzz { }
// this ghost module has 1 name in top level
declare namespace NodeJS {
interface Foo { }
interface Bar { }
interface Buzz { }
}
// safety!
var n = NodeJS; // TS Error : Could not find symbol NodeJS
````
This also allows you to open up further customization in external modules as interfaces declared *inside* external module declarations cannot be extended. The following is a good example as people can customize `foo` further in other library definitions.
```typescript
// Usage when declaring an external module
declare module 'foo' {
var foo: NodeJS.Foo;
export = foo;
}
```
### Extending built-in types
There isn't a way to add *static members* to native objects at the moment as `lib.d.ts` defines them as a `var Date:{/*members*/}` and `var`s are not extendable.
Two solutions are proposed to the TS team. Either [use interfaces instead of var in lib.d.ts (vote)](https://web.archive.org/web/20170624125029/https://typescript.codeplex.com/workitem/1085) and/or [make variables/classes open ended (vote)](https://web.archive.org/web/20170624162922/https://typescript.codeplex.com/workitem/917)
For adding members to *instances* of native types there are relevant interfaces in available in `lib.d.ts` e.g.
```typescript
// add members to Date instances
interface Date {
newMember: number;
}
// usage
var foo = new Date();
foo.newMember = 123; // okay
```
### Getter / Setter
Instead of :
```typescript
declare function duration(value?: number): any;
```
better to do:
```typescript
declare function duration(): number;
declare function duration(value: number): void;
```
### Fluent
Pretty self explanatory:
```typescript
interface Something {
foo(): Something;
bar(): Something;
}
```
### Callback signatures
Do not mark callback arguments as optional if they are passed in everytime by the calling code. Also leave the return as `any` if the calling code doesn't care. For example in the following *good* declaration `foo` is the calling code we are declaring that always calls with `bar` and `bas` and doesn't care of the callback return value:
```typescript
declare function foo(callback: (bar: any, bas: any) => any): void;
// Usage is as expected by a JavaScript developer
foo(() => { });
foo((bar) => 123);
foo((bar, bas) => '');
```
A *wrong* way to model it would be as shown below as it enforces restrictions the original calling code doesn't impose:
```typescript
declare function foo(callback: (bar?: any, bas?: any) => void);
```
### Function Overloading
A Union Type (`any` for now) is needed only for config object bags. For functions / constructors use function overloading e.g.
```typescript
declare class Foo {
constructor(foo: number);
constructor(foo: string);
}
new Foo(123); // okay
new Foo('123'); // okay
new Foo(true); // Error
```
### Overload Ordering
Code with overloads *must* be manually sorted from the tightest/more-specific overload to loosest. See example below:
```typescript
interface Parent { x; }
interface Child extends Parent { y; }
function foo(p: Child): Child;
function foo(p: Parent): Parent;
function foo(p: any): any;
function foo(p: any) { return p; }
var a = foo({ x: 3, y: 4 }); // a: Child
var b = foo({ x: 5 }); // b: Parent
var y: any;
var c = foo(y); // c: any
```
[Related issue on Codeplex](https://web.archive.org/web/20170624122025/https://typescript.codeplex.com/workitem/2442)