是否可以仅使用接口而不是类来模拟TypScript的多态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()构造函数不进行类型判断,因为来自go struct 化newFoo()foo()的返回类型被推断为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仍然正确输入.

有什么 idea 吗?

推荐答案

polymorphic this type隐含着generic.this类型是F-bounded polymorphism的特殊类型,在C++中称为"curiously recurring template pattern".对于类型实现者来说,this是"我正在编写的当前类型的某个通用子类型",而对于类型userthis就是该类型. 如果您想实现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; }

您需要foobar才能返回this,而不是FooBar.这意味着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 link to code

Typescript相关问答推荐

Angular -使用Phone Directive将值粘贴到控件以格式化值时验证器不工作

如何为ViewContainerRef加载的Angular组件实现CanDeactivate保护?

类型安全JSON解析

如何创建泛型类型组件来使用React Native的FlatList或SectionList?'

忽略和K的定义

在配置对象上映射时,类型脚本不正确地推断函数参数

更新为Angular 17后的可选链运算符&?&问题

如何使用Zod使一个基于其他字段值的字段为必填字段?

在函数中打字推断记录的关键字

从子类构造函数推断父类泛型类型

React Typescript项目问题有Redux-Toolkit userSlice角色问题

刷新页面时,TypeScrip Redux丢失状态

保护函数调用,以便深度嵌套的对象具有必须与同级属性函数cargument的类型匹配的键

为什么我在这个重载函数中需要`as any`?

如何获取受类型脚本泛型约束的有效输入参数

在类型脚本中将泛型字符串数组转换为字符串字母并集

如何编辑切片器以使用CRUD调用函数?

如何静态键入返回数组1到n的函数

接口中可选嵌套属性的类型判断

如何为对象创建类型,其中键是只读且动态的,但值是固定类型?