【问题标题】:Typescript static methods in interfaces接口中的打字稿静态方法
【发布时间】:2021-04-27 00:28:53
【问题描述】:

我正在寻找一种方法来要求一个类拥有一些静态方法,而不必自己实现它们(就像一个接口可以定义普通方法一样)。由于接口不支持静态方法,所以下面的代码不起作用。

interface MyInterface {
  static fromJSON(json: string): MyInterface
  toJSON(): object
}

抽象类不是我想要的东西,因为它们不需要开发人员自己编写方法,但我必须实现它。
有没有类似的东西,不用写很多自定义逻辑?

使用上面的接口,不应接受以下实现:

class MyClass implements MyInterface {
  // Missing static method "fromJSON"
  toJSON() {
    return {}
  }
}

这个也不应该:

class MyClass implements MyInterface {
  static fromJSON(json: string) {
    return 123 // Wrong type
  }

  toJSON() {
    return {}
  }
}

但是这个应该被接受:

class MyClass implements MyInterface {
  static fromJSON(json: string) {
    return new MyClass()
  }

  toJSON() {
    return {}
  }
}

【问题讨论】:

  • 这在本质上是不可能的。不过,我不了解确切的用例,所以我不知道是否有解决方法。假设您在谈论普通方法……您如何“要求”一个类拥有这些方法?通过写class Foo implements MyInterface { ... }?或者你有一个接受类实例的函数吗?还是接受类构造函数的函数?你能写一个minimal reproducible example,包括你想禁止的东西和你想接受的东西吗?
  • 也许this 会有所帮助

标签: javascript node.js typescript


【解决方案1】:

TypeScript 对限制类的静态方面确实没有太多支持。这是一个缺失的功能;请参阅microsoft/TypeScript#14600 获取整体功能请求,以及 microsoft/TypeScript#33892 仅用于“支持 static implements 类”部分,microsoft/TypeScript#34516 仅用于“支持abstract static 类成员”部分。


对于您所展示的形式的 static 成员 interface 的一个大障碍是,类型系统很难以一种实际执行您想要的方式来理解它。有一个长期悬而未决的问题,microsoft/TypeScript#3841,要求类的 constructor 属性应该是强类型的。目前它只有Function的类型:

class Foo {
  instanceProp: string = "i"
  static staticProp: string = "s"
}
const foo = new Foo();
foo.constructor.staticProp; // error!
// -----------> ~~~~~~~~~~ 
// Property 'staticProp' does not exist on type 'Function'

有一些棘手的原因说明为什么这不容易做到,在问题中说明了,但本质上问题是子类构造函数不需要是父类构造函数的真正子类型:

class Bar extends Foo {
  subInstanceProp: string;
  constructor(subInstanceProp: string) {
    super();
    this.subInstanceProp = subInstanceProp;
  }
}
const bar = new Bar("hello");

这里,Bar 构造函数的类型为new (subInstanceProp: string) => Bar,它不能分配给Foo 构造函数的类型new () => Foo。通过extendsbar 应该可以分配给Foo。但是如果bar.constructor 不能分配给Foo['constructor'],那么一切都会中断。

可能有办法解决这个问题,但到目前为止还没有实现。

所有这一切意味着没有办法查看MyInterface 类型的对象并确保构造它的对象具有fromJSON 方法。因此,在 interface 定义中包含 static 并没有真正起到任何有用的作用。


microsoft/TypeScript#33892 和 microsoft/TypeScript#34516 中的请求没有这个问题。如果你能这样写:

class MyClass implements MyInterface static implements MyInterfaceConstructor {
// not valid TS, sorry ------------> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  toJSON() { return "" };
  static fromJSON(json: string) { return new MyClass() };
}

或者这个:

abstract class MyAbstractClass {
  abstract toJSON(): string;
  abstract static fromJSON(json: string): MyAbstractClass
// ------> ~~~~~~
// not valid TS, sorry
}

你有办法做到这一点。唉,从 TS4.1 开始,这些功能都没有实现,所以唯一的方法是使用变通方法。


让我们使用我上面写的MyInterfaceMyInterfaceConstructor 接口,看看我们可以用它们做什么。目前我们只能通过implements MyInterface约束实例端:

class MyClass implements MyInterface {
  toJSON() { return "" };
  static fromJSON(json: string) { return new MyClass() };
}

我们不能写static implements MyInterfaceConstructor。但是我们可以创建一个名为 staticImplements 的无操作辅助函数并调用它:

function staticImplements<T>(ctor: T) { }

staticImplements<MyInterfaceConstructor>(MyClass); // okay

这个编译没有错误的事实是你保证MyClass的静态端是可以接受的。在运行时这是一个空操作,但在编译时这是有价值的信息。让我们看看当我们做错了会发生什么:

class MyClassBad implements MyInterface {
  toJSON() {
    return ""
  }
}
staticImplements<MyInterfaceConstructor>(MyClassBad); // error!
// ------------------------------------> ~~~~~~~~~~
// Property 'fromJSON' is missing in type 'typeof MyClassBad' 
// but required in type 'MyInterfaceConstructor'.

class MyClassAlsoBad implements MyInterface {
  static fromJSON(json: string) {
    return 123 // Wrong type
  }
  toJSON() {
    return ""
  }
}
staticImplements<MyInterfaceConstructor>(MyClassAlsoBad); // error!
// ------------------------------------> ~~~~~~~~~~~~~~
// The types returned by 'fromJSON(...)' are incompatible between these types.
function validMyClass(ctor: MyInterfaceConstructor) { }

这些是您要查找的错误。是的,静态约束和错误并不完全位于您希望它们在代码中的位置,但至少您可以表达这一点。这是一种解决方法。

此解决方法还有其他版本,可能使用装饰器(在 JS 中的装饰器支持最终确定之前,这些装饰器已被弃用或暂停),但这是基本思想:尝试将构造函数类型分配给“静态部分” " 你的界面,看看有没有什么失败。

Playground link to code

【讨论】:

    猜你喜欢
    • 2019-07-30
    • 2020-09-11
    • 2017-09-11
    • 1970-01-01
    • 1970-01-01
    • 2017-08-20
    • 2019-01-24
    • 2016-08-28
    相关资源
    最近更新 更多