一、上下文类型
(1)上下文类型的核心理念:基于位置的类型推导。
window.onerror = (event, source, line, col, err) => {};
(2)在这个例子里,虽然我们并没有为 onerror 的各个参数声明类型,但是它们也已经获得了正确的类型。当然你肯定能猜到,这是因为 onerror 的类型声明已经内置了:
interface Handler {
// 简化
onerror: OnErrorEventHandlerNonNull;
}
interface OnErrorEventHandlerNonNull {
(event: Event | string, source?: string, lineno?: number, colno?: number, error?: Error): any;
}
(2)相对于提到的基于开发者输入进行的类型推导,上下文类型更像是反方向的类型推导,也就是基于已定义的类型来规范开发者的使用。
(3)在上下文类型中,我们实现的表达式可以只使用更少的参数,而不能使用更多,这还是因为上下文类型基于位置的匹配,一旦参数个数超过定义的数量,那就没法进行匹配了。
// 正常
window.onerror = (event) => {};
// 报错
window.onerror = (event, source, line, col, err, extra) => {};
二、void 返回值类型下的特殊情况
type CustomHandler = (name: string, age: number) => void;
const handler1: CustomHandler = (name, age) => true;
const handler2: CustomHandler = (name, age) => 'linbudu';
const handler3: CustomHandler = (name, age) => null;
const handler4: CustomHandler = (name, age) => undefined;
(1)上下文类型对于 void 返回值类型的函数,并不会真的要求它啥都不能返回。然而,虽然这些函数实现可以返回任意类型的值,但对于调用结果的类型,仍然是 void:
const result1 = handler1('linbudu', 599); // void
const result2 = handler2('linbudu', 599); // void
const result3 = handler3('linbudu', 599); // void
const result4 = handler4('linbudu', 599); // void
(2)看起来这是一种很奇怪的、错误的行为,但实际上,我们日常开发中的很多代码都需要这一“不正确的”行为才不会报错,比如以下这个例子:
const arr: number[] = [];
const list: number[] = [1, 2, 3];
list.forEach((item) => arr.push(item));
1、这是我们常用的简写方式,然而,push 方法的返回值是一个 number 类型(push 后数组的长度),而 forEach 的上下文类型声明中要求返回值是 void 类型。如果此时 void 类型真的不允许任何返回值,那这里我们就需要多套一个代码块才能确保类型符合了。
2、但这真的是有必要的吗?对于一个 void 类型的函数,我们真的会去消费它的返回值吗?既然不会,那么它想返回什么,全凭它乐意就好了。我们还可以用另一种方式来描述这个概念:你可以将返回值非 void 类型的函数(() => list.push()
)作为返回值类型为 void 类型(arr.forEach
)的函数类型参数。