如何正确检测泛型类型是否继承另一个泛型类型?
处理泛型协变/逆变下类型兼容性检测的更优方案
首先明确问题根源:TypeScript 中,对象属性的泛型参数是协变的,所以 Bar<'a string'> 能继承 Bar<string>;但函数参数的泛型参数是逆变的,这意味着 Foo<string> 要求参数是 string 的超类型(能接受所有字符串),而 Foo<'a string'> 仅接受字面量子类型,因此 Foo<'a string'> 无法继承 Foo<string>。
以下是比你提到的两种方案更优的解决思路:
方案1:复用型泛型参数检测工具
自定义一个通用工具类型,提取泛型内部的类型参数并验证其是否符合目标类型,避免重复编写 infer 逻辑:
// 针对 Foo<T> 的专用检测工具 type CheckFooExtends<T, Target> = T extends Foo<infer Inner> ? Inner extends Target ? 'As Expected' : 'Surprising' : 'Surprising'; // 使用示例 type Foo4 = CheckFooExtends<Foo<'a string'>, string>; // 'As Expected'
如果是多泛型复合类型(比如 Baz<T, U> = { prop: T; fn: (a: U) => void }),可以扩展工具同时检测多个参数:
type CheckBazExtends<T, TargetT, TargetU> = T extends Baz<infer InnerT, infer InnerU> ? (InnerT extends TargetT && InnerU extends TargetU) ? 'As Expected' : 'Surprising' : 'Surprising';
方案2:用内置工具直接检测底层类型
针对函数类泛型,利用 TypeScript 内置的 Parameters 和 ReturnType 工具,绕过协变/逆变的影响,直接检查参数和返回值类型:
type Foo5 = (Parameters<Foo<'a string'>>[0] extends string && ReturnType<Foo<'a string'>> extends string) ? 'As Expected' : 'Surprising'; // 'As Expected'
这种方式无需自定义复杂逻辑,可读性更强,适合函数类型的场景。
方案3:安全的中性基类型替代 any
你之前用 (a: any) => any 作为基类型不够安全,改用 unknown(TypeScript 的顶级类型)可以在保持兼容性的同时维持类型安全:
type SafeFooBase = (a: unknown) => unknown; type Foo6 = Foo<'a string'> extends SafeFooBase ? 'As Expected' : 'Surprising'; // 'As Expected'
unknown 既允许任何类型赋值给它(对应协变的返回值),也允许它赋值给任何类型(对应逆变的参数),同时避免了 any 带来的类型逃逸风险。
内容的提问来源于stack exchange,提问作者TrevTheDev
相关产品推荐
相关产品推荐

