Do's & Don'ts
该做什么和不该做什么
一般类型
Number,String,Boolean,和Object
不要
用不完的类型Number
,String
,Boolean
,或Object
。这些类型是指在JavaScript代码中几乎从不使用的非原始装箱对象。
/* WRONG */
function reverse(s: String): String;
不要
使用类型number
,string
和boolean
。
/* OK */
function reverse(s: string): string;
而不是Object
使用非原始object
类型(在TypeScript 2.2中添加)。
泛型
永远不要
有一个不使用其类型参数的泛型类型。在TypeScript FAQ页面查看更多详细信息。
回调类型
返回类型的回调
不要
使用返回类型any
的值将被忽略的回调:
/* WRONG */
function fn(x: () => any) {
x(
}
不要
使用返回类型void
的回调,其价值将被忽略:
/* OK */
function fn(x: () => void) {
x(
}
原因
:使用void
更安全是因为它可以防止您意外使用x
未经检查的返回值:
function fn(x: () => void) {
var k = x( // oops! meant to do something else
k.doSomething( // error, but would be OK if the return type had been 'any'
}
回调中的可选参数
不要
在回调中使用可选参数,除非您确实如此:
/* WRONG */
interface Fetcher {
getObject(done: (data: any, elapsedTime?: number) => void): void;
}
这具有非常明确的含义:done
可以用1个参数调用回调,也可以用2个参数调用回调。作者可能打算说回调可能不关心elapsedTime
参数,但是不需要使参数成为可选参数来实现这一点 - 提供接受更少参数的回调总是合法的。
请将
回调参数编写为非可选:
/* OK */
interface Fetcher {
getObject(done: (data: any, elapsedTime: number) => void): void;
}
重载和回调
不要
编写仅在回调函数上不同的重载:
/* WRONG */
declare function beforeAll(action: () => void, timeout?: number): void;
declare function beforeAll(action: (done: DoneFn) => void, timeout?: number): void;
不要
使用最大元数写一个过载:
/* OK */
declare function beforeAll(action: (done: DoneFn) => void, timeout?: number): void;
原因
:回调忽略参数总是合法的,因此不需要较短的过载。首先提供一个较短的回调允许错误类型的函数被传入,因为它们匹配第一个重载。
功能过载
订购
不要
在更具体的重载之前放置更多的一般重载:
/* WRONG */
declare function fn(x: any): any;
declare function fn(x: HTMLElement): number;
declare function fn(x: HTMLDivElement): string;
var myElem: HTMLDivElement;
var x = fn(myElem // x: any, wat?
不要
通过将更多的普通签名更具体的签名后排序重载:
/* OK */
declare function fn(x: HTMLDivElement): string;
declare function fn(x: HTMLElement): number;
declare function fn(x: any): any;
var myElem: HTMLDivElement;
var x = fn(myElem // x: string, :)
原因
:TypeScript 在解析函数调用时选择第一个匹配的重载
。当一个较早的过载比另一个更“一般”时,后一个过载是有效隐藏的,不能被调用。
使用可选参数
不要
写出仅在拖尾参数上有所不同的重载:
/* WRONG */
interface Example {
diff(one: string): number;
diff(one: string, two: string): number;
diff(one: string, two: string, three: boolean): number;
}
请
尽可能使用可选参数:
/* OK */
interface Example {
diff(one: string, two?: string, three?: boolean): number;
}
请注意,只有当所有重载具有相同的返回类型时,才会发生崩溃。
为什么
:这有两个重要的原因。
TypeScript通过查看是否可以使用源的参数调用目标的任何签名来解析签名兼容性,并且允许使用无关的参数
。例如,此代码仅在使用可选参数正确书写签名时暴露了一个错误:
function fn(x: (a: string, b: number, c: number) => void) { }
var x: Example;
// When written with overloads, OK -- used first overload
// When written with optionals, correctly an error
fn(x.diff
第二个原因是消费者使用TypeScript的“strict null checking”功能。因为未指定的参数undefined
在JavaScript中显示,所以将显式传递undefined
给具有可选参数的函数通常可以。例如,这个代码在严格的空值下应该是OK的:
var x: Example;
// When written with overloads, incorrectly an error because of passing 'undefined' to 'string'
// When written with optionals, correctly OK
x.diff("something", true ? undefined : "hour"
使用联合类型
不要
仅在一个参数位置写入类型不同的重载:
/* WRONG */
interface Moment {
utcOffset(): number;
utcOffset(b: number): Moment;
utcOffset(b: string): Moment;
}
不要
使用联合类型时参考:
/* OK */
interface Moment {
utcOffset(): number;
utcOffset(b: number|string): Moment;
}
请注意,b
由于签名的返回类型不同,因此我们没有在此处进行选择。
为什么
:这对于为你的功能“传递”价值的人很重要:
function fn(x: string): void;
function fn(x: number): void;
function fn(x: number|string) {
// When written with separate overloads, incorrectly an error
// When written with union types, correctly OK
return moment().utcOffset(x
}