簡體   English   中英

接口中的 Typescript static 方法

[英]Typescript static methods in interfaces

我正在尋找一種方法來要求 class 擁有一些 static 方法,而無需自己實現它們(就像接口可以定義普通方法一樣)。 由於接口不支持 static 方法,因此以下代碼不起作用。

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

抽象類不是我想要的東西,因為它們不需要開發人員自己編寫方法,但我必須實現它。
有沒有類似的東西,無需編寫大量自定義邏輯?

使用上面的接口,不應接受以下實現:

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

這個也不應該:

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

  toJSON() {
    return {}
  }
}

但是這個應該被接受:

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

  toJSON() {
    return {}
  }
}

TypeScript 中確實沒有太多支持來約束 class 的 static 端。 這是一個缺失的功能; see microsoft/TypeScript#14600 for an overall feature request, as well as microsoft/TypeScript#33892 for just the "support static implements on classes" part, and microsoft/TypeScript#34516 for just the "support abstract static class members" part.


對於您所顯示的形式的interfacestatic成員之類的東西,一個很大的障礙是類型系統很難以一種實際上會做您想要的方式來理解它。 有一個長期懸而未決的問題, microsoft/TypeScript#3841 ,要求 class 的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 構造函數的真正子類型:

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 類型的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 並將其命名為:

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

staticImplements<MyInterfaceConstructor>(MyClass); // okay

編譯沒有錯誤的事實是您保證MyClass的 static 端是可以接受的。 在運行時這是一個空操作,但在編譯時這是有價值的信息。 讓我們看看當我們做錯了會發生什么:

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) { }

這些是您正在尋找的錯誤。 是的,static 約束和錯誤並不完全位於代碼中您想要的位置,但至少您可以表達這一點。 這是一種解決方法。

此解決方法還有其他版本,可能使用裝飾器(在 JS 中的裝飾器支持最終確定之前,這些裝飾器已被棄用或保留),但這是基本思想:嘗試將構造函數類型分配給您的“靜態部分”界面,看看有沒有什么問題。

Playground 代碼鏈接

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM