TypeScript函数参数逆变与any类型的矛盾结果解析
Func<Admin> extends Func<any>为true,而Func<Admin> extends Func<User>为false? 先明确问题对应的代码场景,理清类型关系:
class User {} class Admin extends User {} type Func<T> = (arg: T) => void; type IsSubtypeOf<T, U> = T extends U ? true : false; // 结果:false,符合逆变规则 type Case1 = IsSubtypeOf<Func<Admin>, Func<User>>; // 结果:true,看似矛盾 type Case2 = IsSubtypeOf<Func<Admin>, Func<any>>;
核心原因在于TypeScript对any类型的特殊兼容性规则,它和普通父子类型(比如User与Admin)的逻辑完全不同:
普通父子类型的逆变逻辑:
函数参数的逆变规则是:如果A是B的子类型,那么(B) => void是(A) => void的子类型。
在Case1中,Admin是User的子类型,所以Func<User>(即(User)=>void)是Func<Admin>(即(Admin)=>void)的子类型;反过来Func<Admin>并不是Func<User>的子类型,因此IsSubtypeOf<Func<Admin>, Func<User>>返回false,这完全符合逆变规则。any类型的特殊兼容性:
any是TypeScript的特殊顶级类型,它的兼容性是双向的——任何类型都可以被判定为any的子类型,同时any也可以被判定为任何类型的子类型(除了never)。
对于函数类型来说,当参数是any时,TypeScript不会严格执行逆变检查:(arg: Admin) => void可以安全赋值给(arg: any) => void,因为调用后者时传入的any类型参数,完全可以被前者的Admin类型参数接受(any兼容所有类型)。因此Func<Admin>会被判定为Func<any>的子类型,IsSubtypeOf<Func<Admin>, Func<any>>返回true。
简单来说,any的设计初衷就是绕过常规类型检查,它的兼容性规则不受普通逆变/协变逻辑的约束,这才导致了看似矛盾的结果。
内容的提问来源于stack exchange,提问作者user20508163

