TypeScript鸭子类型是否排除类构造函数及PickFromConstructor疑问
问题解析与解答
首先假设你的代码实现大致如下(符合问题描述的场景):
type PickFromConstructor<T extends new (...args: any[]) => any, K extends keyof T> = Pick<T, K> & new (...args: ConstructorParameters<T>) => InstanceType<T>; class A { static optionalProp?: string; static requiredProp: number = 1; instanceProp: boolean = true; constructor(public name: string) {} } type PickedA = PickFromConstructor<A, 'requiredProp'>; // 底部赋值示例 const picked: PickedA = A; // 六个Extends测试示例 type Test1 = A extends PickedA ? true : false; type Test2 = PickedA extends A ? true : false; type Test3 = InstanceType<A> extends InstanceType<PickedA> ? true : false; type Test4 = InstanceType<PickedA> extends InstanceType<A> ? true : false; type Test5 = typeof A extends PickedA ? true : false; type Test6 = PickedA extends typeof A ? true : false;
1. 类型A相较于PickedA缺失了哪些属性?
需要明确:A是类的类型(同时包含构造函数签名和所有静态属性),PickedA是通过PickFromConstructor生成的类型。二者的实际差异是:
PickedA仅保留了A中指定的静态属性requiredProp,以及A的构造函数签名;- 因此并非
A缺失属性,而是PickedA缺失了A的静态可选属性optionalProp。
2. 当构造函数类型的InstanceType包含额外属性时,差异产生的原因是什么?
TypeScript的构造函数类型(如new () => T)是声明式类型,其InstanceType<T>是显式指定的实例类型;而实际类的实例类型是推导式结构类型。当二者不一致时:
- 若构造函数类型的
InstanceType包含实际类实例没有的属性,PickFromConstructor会基于这个声明的InstanceType约束返回的构造函数实例类型; - 但实际类的实例并不具备这些额外属性,这就导致类型层面的约束和运行时实际结构的差异——TypeScript会按声明类型做检查,但运行时访问这些额外属性会得到
undefined或报错。
本质是TypeScript结构类型系统中,「声明类型」与「实际推导类型」不匹配导致的差异。
3. 文件底部的赋值为何没有报错?
const picked: PickedA = A;不报错的核心是TypeScript的结构类型兼容性规则:
PickedA的结构是「包含requiredProp静态属性 + 与A一致的构造函数签名」;A的类型完全满足这个结构(A既有requiredProp,又有对应的构造函数);- 即
A的类型是PickedA的超类型,超类型可以赋值给子类型,因此不会触发错误。
4. 为何六个...Extends...类型的结果与预期不符?
先明确上述示例中六个测试的实际结果:
Test1 = true(A extends PickedA):A包含PickedA的所有属性,满足子类型约束;Test2 = false(PickedA extends A):PickedA缺少A的optionalProp静态属性,不满足;Test3 = true(InstanceType<A> extends InstanceType<PickedA>):二者实例类型完全一致;Test4 = true(同上,双向兼容);Test5 = true(typeof A就是A的类型,同Test1);Test6 = false(同Test2)。
如果你的预期与实际结果不符,通常是两个原因:
- 混淆了超类型与子类型的判断逻辑:你可能误以为
PickedA是A的子类型就应该PickedA extends A为true,但实际上子类型必须包含父类型的所有属性,PickedA缺少optionalProp,因此不满足; PickFromConstructor实现存在缺陷:比如错误地将K extends keyof InstanceType<T>(实例属性)而非keyof T(静态属性),或者构造函数签名的参数/实例类型处理错误,导致生成的PickedA结构与预期不符,进而影响extends的结果。
内容的提问来源于stack exchange,提问作者Lemon Lord
相关产品推荐
相关产品推荐

