带映射类型的泛型为何允许null?两类泛型类差异解析
泛型类
Foo与Bar的差异分析 先看完整代码示例:
class Foo<T extends {[K in keyof T]: any}> { foo!: T; } declare const group: Foo<{foo: string}>; // OK declare const group2: Foo<null>; // OK type obj = {[K in keyof null]: any} // ^? {} class Bar<T extends {}> { bar!: T; } declare const group3: Bar<{bar: string}>; // OK declare const group4: Bar<null>; // KO
两者的核心差异在于泛型约束的逻辑:
1. Bar类的约束T extends {}
在TypeScript中,{}类型表示能被当作对象的非原始类型,严格模式下会直接排除null、undefined,以及字符串、数字、布尔这些原始类型。null不属于{}的子类型,所以Bar<null>会触发类型检查错误。
2. Foo类的约束T extends {[K in keyof T]: any}
这是一个自引用的映射类型约束,它基于T自身的键集生成一个新类型,再要求T继承这个类型:
- 当
T=null时,keyof null返回never(因为null没有任何可枚举属性),所以映射类型{[K in never]: any}最终会生成空对象类型{}。 - 因为这个约束是依赖
T自身推导出来的,TypeScript会对null/undefined这类特殊值做特殊处理——尽管null本身不继承{},但由于约束是从null自身生成的,TypeScript判定该约束满足,因此Foo<null>可以通过编译。
总结:Foo的约束几乎没有限制,能接受所有类型包括null;而Bar的约束明确限定T必须是对象类型,直接排除了null。
内容的提问来源于stack exchange,提问作者Matthieu Riegler
相关产品推荐
相关产品推荐

