TypeScript strictFunctionTypes模式下接口继承报错原因解析
TypeScript strictFunctionTypes模式下泛型接口继承的类型错误解析
示例代码
interface I { x: number; } interface A { cb: (x: I) => void; } interface B<T extends I> extends A { cb: (x: T) => void; }
问题
开启strictFunctionTypes模式时,声明泛型接口B会触发TS错误2430,关闭该模式则无错误。明明T extends I表示T是至少满足I约束的类型,为何仍会出现继承报错?
错误提示
Interface 'B<T>' incorrectly extends interface 'A'. Types of property 'cb' are incompatible. Type '(x: T) => void' is not assignable to type '(x: I) => void'. Types of parameters 'x' and 'x' are incompatible. Type 'I' is not assignable to type 'T'. 'I' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'I'.(2430)
原因解析
1. strictFunctionTypes的核心作用
这个模式会禁用函数参数的双向协变,强制仅遵循函数参数的逆变规则;而关闭该模式时,TypeScript默认允许双向协变(既支持逆变也支持协变),这是早期为兼容旧代码保留的宽松行为。
2. 函数参数的逆变安全规则
对于函数类型的赋值,安全逻辑是:目标函数的参数类型若为X,源函数的参数类型必须是X的父类型(逆变),这样源函数能接受目标函数允许的所有参数,避免运行时错误。
回到示例:
- 接口
A的cb类型是(x: I) => void,意味着它可以接受任何符合I的类型(包括纯I对象,或I的子类型对象)。 - 接口
B<T>的cb类型是(x: T) => void,它只能接受符合T的类型。T extends I仅说明T是I的子类型,但T可能包含I没有的额外属性(比如T = {x: number, y: string})。
若允许B<T>继承A,相当于断言(x: T) => void可以赋值给(x: I) => void。但实际场景中,当调用A的cb传入纯I对象(比如{x: 1})时,B的cb会收到缺少T额外属性的参数,这会引发潜在的运行时错误——这就是TypeScript严格模式下阻止该继承的原因。
3. 错误提示的深层含义
错误提示里的Type 'I' is not assignable to type 'T',本质是说:T可以是I的任意子类型,当T是带额外属性的I子类型时,纯I对象并不满足T的类型要求,因此无法将I赋值给T。而(x: T) => void作为源函数,无法接受目标函数(x: I) => void允许的所有参数,所以类型不兼容。
内容的提问来源于stack exchange,提问作者Adam Baranyai
相关产品推荐
相关产品推荐

