应该写出回调函数的非可选参数:
/* OK */
interface Fetcher {
getObject(done: (data: any, elapsedTime: number) => void): void;
}
重载与回调函数
不要因为回调函数参数个数不同而写不同的重载:
/* 错误 */
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;
为什么:回调函数总是可以忽略某个参数的,因此没必要为参数少的情况写重载。 参数少的回调函数首先允许错误类型的函数被传入,因为它们匹配第一个重载。
函数重载
顺序
不要把一般的重载放在精确的重载前面:
/* 错误 */
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会选择第一个匹配到的重载当解析函数调用的时候。 当前面的重载比后面的“普通”,那么后面的被隐藏了不会被调用。
使用可选参数
不要为仅在末尾参数不同时写不同的重载:
/* 错误 */
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解析签名兼容性时会查看是否某个目标签名能够使用源的参数调用, 且允许外来参数。 下面的代码暴露出一个bug,当签名被正确的使用可选参数书写时:
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“严格检查null”特性时。 因为没有指定的参数在JavaScript里表示为undefined
,通常显示地为可选参数传入一个undefined
。 这段代码在严格null模式下可以工作:
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
成为可选的,因为签名的返回值类型不同。
为什么:This is important for people who are "passing through" a value to your function:
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);
}
标签:TypeScript
相关阅读 >>
更多相关阅读请进入《typescript》频道 >>

Vue.js 设计与实现 基于Vue.js 3 深入解析Vue.js 设计细节
本书对 Vue.js 3 技术细节的分析非常可靠,对于需要深入理解 Vue.js 3 的用户会有很大的帮助。——尤雨溪,Vue.js作者