TypeScript 在接口中实现此类型的多态性

问题描述 投票:0回答:1

是否可以仅使用接口而不是类来模拟 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
保持正确键入。

有什么想法吗?

typescript this
1个回答
0
投票

多态

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
的多态性得到了保持。

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.