如何让TypeScript基于泛型类型状态实现方法的条件可用?
问题描述
我用TypeScript实现了一个带泛型状态的基类BaseClass,通过泛型IsManual切换行为,包含manually()方法更新内部_isManually状态。子类TestBaseClass的callRequest()方法需要根据状态返回不同类型:IsManual=true时返回Request实例,否则返回Promise<Response>。
但调用t.manually().callRequest().request()时,TypeScript报错Property 'request' does not exist on type 'Request | Promise<Response>',即使手动设置了条件返回类型,类型系统也无法根据状态识别可用方法。
原始代码:
type ManualValue = true | false; export class Request { request() { return fetch('SOME_ROUTE'); } foo() { return 1; } } export default class BaseClass<IsManual extends ManualValue = false> { _isManually: IsManual = false as IsManual; manually(): Omit<this, keyof BaseClass<any>> & BaseClass<true>; manually(value: true): Omit<this, keyof BaseClass<any>> & BaseClass<true>; manually(value: false): Omit<this, keyof BaseClass<any>> & BaseClass<false>; manually(value?: ManualValue): BaseClass<ManualValue> { if (value === undefined) { this._isManually = true as IsManual; } else { this._isManually = value as IsManual; } return this as BaseClass<IsManual>; } } export class TestBaseClass extends BaseClass { callRequest(payload?: any): this['_isManually'] extends true ? Request : Promise<Response> { const request = new Request(); if (this._isManually) { return request as any; } return request.request() as any; } } const t = new TestBaseClass(); t.manually().callRequest().request(); // 报错
问题原因
- 泛型与实例属性脱节:
this['_isManually']是实例属性的类型,TypeScript无法将运行时的属性值变化关联到类的泛型参数上,因此条件类型无法根据manually()的调用动态调整。 - 子类泛型丢失:
TestBaseClass extends BaseClass没有显式声明泛型参数,导致类的泛型被固定为默认的false,无法随manually()方法更新泛型状态。
解决方案
要让TypeScript正确跟踪状态,需要让类的泛型参数直接控制返回类型,同时确保manually()方法返回的子类实例携带正确的泛型参数:
- 子类显式继承带泛型的基类:声明
TestBaseClass的泛型参数,继承BaseClass<IsManual>。 - 让callRequest依赖类的泛型参数:将返回类型改为
IsManual extends true ? Request : Promise<Response>,直接关联类的泛型状态。 - 修正manually方法的返回类型:确保
manually()返回当前子类的实例,且泛型参数被正确切换,使用this类型结合泛型替换实现。
修改后的代码:
type ManualValue = true | false; export class Request { request() { return fetch('SOME_ROUTE'); } foo() { return 1; } } export default class BaseClass<IsManual extends ManualValue = false> { _isManually: IsManual = false as IsManual; manually(): this extends BaseClass<infer T> ? Omit<this, keyof BaseClass<any>> & BaseClass<true> : never; manually(value: true): this extends BaseClass<infer T> ? Omit<this, keyof BaseClass<any>> & BaseClass<true> : never; manually(value: false): this extends BaseClass<infer T> ? Omit<this, keyof BaseClass<any>> & BaseClass<false> : never; manually(value?: ManualValue): this { this._isManually = (value ?? true) as IsManual; return this; } } export class TestBaseClass<IsManual extends ManualValue = false> extends BaseClass<IsManual> { callRequest(payload?: any): IsManual extends true ? Request : Promise<Response> { const request = new Request(); if (this._isManually) { return request as ReturnType<this['callRequest']>; } return request.request() as ReturnType<this['callRequest']>; } } const t = new TestBaseClass(); t.manually().callRequest().request(); // 类型正确,无报错 t.callRequest().then(res => res.json()); // 非手动模式下自动识别为Promise<Response>
说明
- 子类
TestBaseClass通过泛型参数IsManual继承基类,确保泛型状态能被类型系统跟踪。 callRequest的返回类型直接依赖类的泛型参数,当调用manually()后,泛型参数变为true,TypeScript自动推断返回类型为Request,允许调用request()方法;默认状态下泛型为false,返回类型为Promise<Response>,支持Promise链式调用。manually()方法的重载使用条件类型,确保返回的实例是当前子类的类型,且泛型参数被正确切换,保持链式调用的类型连贯性。
内容的提问来源于stack exchange,提问作者Vildan Bina
相关产品推荐
相关产品推荐

