函数
TypeScript 函数
函数类型在 TypeScript 类型系统中扮演着非常重要的角色,它们是可组合系统的核心构建块。
参数注解
TypeScript 中函数的声明与 JavaScript 中保持一致,不过其允许指定额外的类型信息:
let stories: [string, string[]][] = [];
function addStory(title: string, tags: string[]): void {
stories.push([title, tags]);
}
同样可以在 Lambda 表达式中指定类型:
let sortByLength: (x: string, y: string) => number = (x, y) =>
x.length - y.length;
tags.sort(sortByLength);
也可以在函数参数中指定可选参数:
function storySummary(title: string, description?: string) {
if (description) {
return title + description;
} else {
return title;
}
}
或者使用默认值:
function storySummary(title: string, description: string = "") {
return title + description;
}
值得一提的是,当我们确定某个函数并不返回值时,需要注意不能使用 any 来替代 void,以避免误用返回值的情形:
function fn(x: () => void) {
const k = x(); // oops! meant to do something else
k.doSomething(); // error, but would be OK if the return type had been 'any'
}
函数重载
JavaScript 中并不支持函数重载,但是在 TypeScript 中我们可以通过参数的不同实现重载:
declare function createStore(
reducer: Reducer,
preloadedState: PreloadedState,
enhancer: Enhancer
);
declare function createStore(reducer: Reducer, enhancer: Enhancer);
该特性可以帮我们优化代码,譬如:
function padding(a: number, b?: number, c?: number, d?: any) {
if (b === undefined && c === undefined && d === undefined) {
b = c = d = a;
} else if (c === undefined && d === undefined) {
c = a;
d = b;
}
return {
top: a,
right: b,
bottom: c,
left: d
};
}
如果仔细查看代码,就会发现 a,b,c,d 的值会根据传入的参数数量而变化。此函数也只需要 1 个,2 个或 4 个参数。可以使用函数重载来强制和记录这些约束。你只需多次声明函数头。最后一个函数头是在函数体内实际处于活动状态但不可用于外部。
// 重载
function padding(all: number);
function padding(topAndBottom: number, leftAndRight: number);
function padding(top: number, right: number, bottom: number, left: number);
// Actual implementation that is a true representation of all the cases the function body needs to handle
function padding(a: number, b?: number, c?: number, d?: number) {
if (b === undefined && c === undefined && d === undefined) {
b = c = d = a;
} else if (c === undefined && d === undefined) {
c = a;
d = b;
}
return {
top: a,
right: b,
bottom: c,
left: d
};
}
padding(1); // Okay: all
padding(1, 1); // Okay: topAndBottom, leftAndRight
padding(1, 1, 1, 1); // Okay: top, right, bottom, left
padding(1, 1, 1); // Error: Not a part of the available overloads
TypeScript 中的函数重载没有任何运行时开销。它只允许你记录希望调用函数的方式,并且编译器会检查其余代码。
函数类型注解
我们可以使用类型别名或者接口来表示一个可被调用的类型注解:
interface ReturnString {
(): string;
}
它可以表示一个返回值为 string 的函数:
declare const foo: ReturnString;
const bar = foo(); // bar 被推断为一个字符串。
一个接口可提供多种调用签名,用以特殊的函数重载:
interface Overloaded {
(foo: string): string;
(foo: number): number;
}
// 实现接口的一个例子:
function stringOrNumber(foo: number): number;
function stringOrNumber(foo: string): string;
function stringOrNumber(foo: any): any {
if (typeof foo === 'number') {
return foo * foo;
} else if (typeof foo === 'string') {
return `hello ${foo}`;
}
}
const overloaded: Overloaded = stringOrNumber;
// 使用
const str = overloaded(''); // str 被推断为 'string'
const num = overloaded(123); // num 被推断为 'number'
这也可以用于内联注解中:
let overloaded: {
(foo: string): string;
(foo: number): number;
};
Generator | 生成器
function* numbers(): IterableIterator<number> {
console.log("Inside numbers; start");
yield 1;
console.log("Inside numbers; after the first yield");
yield 2;
console.log("Inside numbers; end");
}
// 迭代器结果的类型声明
interface IteratorResult<CompletedType, SuspendedType> {
value: CompletedType | SuspendedType;
done: this is { value: CompletedType };
}