是否可以仅使用接口而不是类来模拟 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'.
}
newBar()
构造函数不会进行类型检查,因为来自解构foo()
的newFoo()
的返回类型被推断为Foo
而不是Bar
。
我发现包装 mixin 的相反方法是有效的:
function fooish<T>(object: T): T & Foo {
return { ...object, foo };
}
function newBar2(): Bar {
return fooish({ bar }); // Typechecks fine
}
但是,在某些情况下,我更喜欢第一种方法,即有一个简单的构造函数,它只返回
Foo
而不是 T & Foo
并通过传播属性而不是应用程序进行组合。本质上,我正在寻找的是接口继承,这样 this
保持正确键入。
有什么想法吗?
this
类型隐含地是通用。 this
类型是 F 有界多态性的特殊类型,称为“C++ 中的奇怪的重复模板模式”。对于类型实现者,this
是“我正在编写的当前类型的一些通用子类型”而对于类型 user,this
就是该类型。如果您想实现 Foo
,则需要确保 foo()
方法返回稍后使用的 Foo
的任何子类型。
您的代码本质上相当于
interface Foo<T extends Foo<T>> {
foo(): T;
}
interface PlainFoo extends Foo<PlainFoo> {}
interface Bar<T extends Bar<T>> extends Foo<T> {
bar(): T;
}
interface PlainBar extends Bar<PlainBar> {}
function foo(this: PlainFoo) { return this; }
function bar(this: PlainBar) { return this; }
function newFoo(): PlainFoo {
return { foo };
}
function newBar(): PlainBar {
return { ...newFoo(), bar };
// Property 'bar' is missing in type 'Foo<PlainFoo>'
// but required in type 'Bar<PlainBar>'
}
现在希望清楚问题所在。你的
newBar()
声称返回一个 PlainBar
,但那是一个 Bar<PlainBar>
,这是一个 Foo<PlainBar>
,因此 foo()
方法需要返回一个 PlainBar
。但它返回的是 PlainFoo
。 this
类型需要在子类中变得更窄,这就是重点。
所以,为了让你的代码能够工作
interface Foo { foo(): this; }
interface Bar extends Foo { bar(): this; }
您需要
foo
和 bar
才能返回 this
,而不是 Foo
和 Bar
。这意味着 this
的泛型需要明确表示为泛型类型参数:
function foo<T extends Foo>(this: T) { return this; }
function bar<T extends Bar>(this: T) { return this; }
现在一切正常了(并且您也不想将
newFoo()
的返回类型注释为 Foo
,因为这会破坏泛型:
function newFoo() {
return { foo };
}
function newBar(): Bar {
return { ...newFoo(), bar };
}
所有这些现在都有效,因为
this
的多态性得到了保持。