TypeScript中约束类型作为函数参数后为何变无约束?t2为何为false?
先看核心代码:
type Person = { [P in string]: any; }; type Student = { grade: number; }; type t0 = Student extends Person ? true : false; // true type t1 = (() => Student) extends () => Person ? true : false; // true type t2 = ((v: Student) => void) extends (v: Person) => void ? true : false; // false
1. 为什么t0是true?
Person是带字符串索引签名的类型,意思是任何字符串类型的键,对应的值都是any。Student只有grade: number这一个属性,而number完全兼容any类型,所以Student的所有属性都符合Person的要求,自然Student extends Person成立,t0为true。
2. 为什么t1是true?
函数的返回值遵循协变规则:如果类型A是B的子类型(A extends B),那么返回A的函数可以被当作返回B的函数来用。
这里Student extends Person,所以() => Student属于() => Person的子类型,t1为true。比如你写了一个返回Student的函数,完全可以把它赋值给要求返回Person的变量,因为Student本身就是Person的一种。
3. 为什么t2是false?
这是因为函数的参数遵循逆变规则——和返回值的协变完全相反:要让(v: A) => void属于(v: B) => void的子类型,必须满足B extends A,而不是A extends B。
回到t2的判断:我们要验证((v: Student) => void)是否是(v: Person) => void的子类型,按照逆变规则,这需要Person extends Student成立,但显然Person是更宽泛的类型(它可以有任意字符串属性,而Student只有grade),Person并不是Student的子类型,所以这个条件不成立,t2为false。
换个实际场景理解:如果有一个函数fn: (v: Person) => void,它能接受任何符合Person的对象(比如带name属性的对象);而(v: Student) => void只能接受有grade属性的Student对象。如果强行把后者当作前者使用,当传入一个没有grade的Person对象时,函数内部访问v.grade就会报错,TypeScript不允许这种不安全的赋值,因此t2为false。
你提到的“约束类型作为函数参数后变为无约束”是个误解,本质是函数参数的类型兼容性规则和普通类型完全相反——普通类型是更具体的兼容更宽泛的,而函数参数要求更宽泛的类型才能兼容更具体的参数类型。
内容的提问来源于stack exchange,提问作者shhhplus

