如何仅用接口模拟TypeScript的多态this类型行为?
仅用接口模拟TypeScript多态
this类型的问题与解决思路 问题场景
想要仅通过接口而非类来实现TypeScript的多态this类型行为,但遇到了类型检查报错。示例代码如下:
interface Foo { foo(): this; } interface Bar extends Foo { bar(): this; } function foo(this: Foo) { return this; } function bar(this: Bar) { return this; } function newFoo(): Foo { return { foo }; } function newBar(): Bar { return { ...newFoo(), bar }; // 报错:Property 'bar' is missing in type 'Foo' but required in type 'Bar'. }
报错原因是newFoo()返回的foo()方法被TypeScript推断为返回Foo类型,而非Bar类型,导致展开后的对象无法匹配Bar接口的类型要求。
已尝试的可行方案:反向混入
通过反向包装混入的方式可以解决类型检查问题,代码如下:
function fooish<T>(object: T): T & Foo { return { ...object, foo }; } function newBar2(): Bar { return fooish({ bar }); // 类型检查通过 }
但更倾向于使用第一种方式:通过仅返回Foo的简单构造函数,用属性展开而非函数调用的方式组合对象,同时保证接口继承和多态this类型的正确性。
解决思路
1. 泛化newFoo构造函数
修改newFoo,让它返回泛型与Foo的交叉类型,这样可以适配任意需要扩展Foo的子类型:
function newFoo<T extends object>(): T & Foo { return { foo } as T & Foo; } function newBar(): Bar { return { ...newFoo<Bar>(), bar }; // 类型检查通过 }
这里newFoo接受泛型参数T,返回T & Foo,当传入Bar作为泛型参数时,newFoo()会返回Bar & Foo类型,和bar属性合并后完全匹配Bar接口,同时foo方法的this类型会被正确推断为Bar,满足多态要求。
2. 类型断言(不推荐)
如果确定对象结构完全符合Bar接口,可以直接给返回值做类型断言跳过检查,但这种方式会失去TypeScript的类型校验保护,仅适合确定结构正确的场景:
function newBar(): Bar { return { ...newFoo(), bar } as Bar; }
3. 调整接口为泛型定义(备选)
把Foo接口改为带泛型参数的形式,指定foo方法的返回类型,不过这种方式会丢失多态this的灵活性,仅作为备选方案:
interface Foo<T> { foo(): T; } interface Bar extends Foo<Bar> { bar(): Bar; } function foo<T extends Foo<T>>(this: T) { return this; } function bar(this: Bar) { return this; } function newFoo(): Foo<Foo<never>> { return { foo }; } function newBar(): Bar { return { ...newFoo() as Foo<Bar>, bar }; }
总结
最推荐的是泛化newFoo构造函数的方案,既保留了属性展开的组合方式,又能让TypeScript正确推断多态this类型,同时保证类型检查的严谨性。
内容的提问来源于stack exchange,提问作者Adam B.
相关产品推荐
相关产品推荐

