一、函数类型兼容规则
函数类型的参数类型使用子类型逆变(允许为其父类型)的方式确定是否成立,而返回值类型使用子类型协变(允许为其子类型)的方式确定。
协变 :子类型可以赋值给父类型的情况
逆变:父类型可以赋值给子类型的情况
type AsFuncArgType<T> = (arg: T) => void;
type AsFuncReturnType<T> = (arg: unknown) => T;
//A≼B:表示A是B的子类
// 1 成立:(T -> Corgi) ≼ (T -> Dog)
type CheckReturnType = AsFuncReturnType<Corgi> extends AsFuncReturnType<Dog>
? 1
: 2;
// 2 不成立:(Dog -> T) ≼ (Animal -> T)
type CheckArgType = AsFuncArgType<Dog> extends AsFuncArgType<Animal> ? 1 : 2;
二、TSConfig 中的 StrictFunctionTypes
(1)StrictFunctionTypes配置项作用:
在比较两个函数类型是否兼容时,将对函数参数进行更严格的检查(When enabled, this flag causes functions parameters to be checked more correctly),而实际上,这里的更严格指的即是 对函数参数类型启用逆变检查。
(2)在禁用了 strictFunctionTypes
的情况下,TypeScript 并不会抛出错误。这是因为,在默认情况下,对函数参数的检查采用 双变( bivariant ) ,即逆变与协变都被认为是可接受的。
(3)在 TypeScript ESLint 中,有这么一条规则:method-signature-style,它的意图是约束在接口中声明方法时,需要使用 property 而非 method 形式:
// method 声明
interface T1 {
func(arg: string): number;
}
// property 声明
interface T2 {
func: (arg: string) => number;
}
1、进行如此约束的原因即,对于 property 声明,才能在开启严格函数类型检查的情况下享受到基于逆变的参数类型检查。
2、对于 method 声明(以及构造函数声明),其无法享受到这一更严格的检查。因为在大部分情况下,我们确实希望方法参数类型的检查可以是双变的,这也是为什么它们的声明中类型结构使用 method 方式来声明:
interface Array<T> {
push(...items: T[]): number;
}