TypeScript类内外调用静态方法的类型推导差异问题
下面的TypeScript示例中,childProxy1.foo无法被TypeScript正确解析,但childProxy2.foo却可以。同样是调用Factory的create方法,为什么通过Parent.create()间接调用和直接调用Child.getFactory().create()会有类型差异?
type ProxyElements = { foo: string } class Parent<T> { protected proxyElements: T constructor(proxyElements: T) { this.proxyElements = proxyElements } public static create<R extends typeof Parent>(this: R){ return this.getFactory().create() } public static getFactory<R extends typeof Parent>(this: R) { return new Factory(this) as Factory<R>; } public getProxy() { return <T & this> new Proxy(this, { get: (target, prop) => { if (prop in this.proxyElements) { return this.proxyElements[prop] } return target[prop] } }) } } class Child extends Parent<ProxyElements> { public childMethod() { return 'qux' } } class Factory<R> { protected classToInstantiate: new (proxyElements) => R constructor(classToInstantiate: new (proxyElements) => R) { this.classToInstantiate = classToInstantiate; } public create() { return (new this.classToInstantiate({foo: 'bar'}) as InstanceType<R>).getProxy() } } // public Factory<Child>.create(): T & Child extends {new(...args: any): infer R} ? R : any const childProxy1 = Child.getFactory().create() childProxy1.foo // Unresolved variable foo // public static Parent<ProxyElements>.create<Child>(): ProxyElements & Child extends {new(...args: any): infer R} ? R : any const childProxy2 = Child.create() childProxy2.foo // Correctly resolved foo
核心原因:泛型类型传递时的上下文丢失
1. 直接调用Child.getFactory().create()的类型问题
当调用Child.getFactory()时,getFactory的泛型R被推断为typeof Child,返回的Factory<R>即Factory<typeof Child>。在Factory.create()方法中,InstanceType<R>只能得到Child的实例类型,但TypeScript无法从typeof Child这个构造函数类型反向推导出Parent类的泛型参数T具体是ProxyElements。因此getProxy返回的T & this中的T处于未解析状态,最终childProxy1的类型里不包含foo属性的定义,导致无法解析。
2. 通过Child.create()间接调用的类型正确原因
Parent.create()方法的泛型R同样是typeof Child,但它处于Parent类的泛型上下文之中。此时Parent的泛型T已经被Child明确填充为ProxyElements,所以getProxy返回的T & this会被TypeScript正确推断为ProxyElements & Child——这个类型包含了foo属性,因此childProxy2.foo能被正常解析。
关键细节:InstanceType<R>的局限性
InstanceType<typeof Child>只能获取Child的实例类型,但无法捕获Child继承的Parent<T>中T的具体类型。而在Parent.create()的上下文中,Parent自身的泛型参数已经被实例化,所以getProxy的返回类型能被完整推导。
内容的提问来源于stack exchange,提问作者Micha Stubi

