TypeScriptインターフェイスで静的プロパティを定義する方法


122

typescriptインターフェイスで静的プロパティを宣言したいだけですか?これについてはどこにも見つかりませんでした。

interface myInterface {
  static Name:string;
}

出来ますか?


正確に何をモデル化しようとしていますか?
— Ryan Cavanaugh、

5
Barがクラスでもある「class Foo implements Bar」の使用を検討してください。TSの他のクラスによってクラスを「実装」できます。
— Tobias Cudnik 2018年

回答:


46

TypeScriptのインターフェイスで静的プロパティを定義することはできません。

Dateの定義に追加しようとするのではなく、オブジェクトを変更したい場合はDate、それをラップするか、単純にリッチな日付クラスを作成して、Date実行しないことを実行できます。

class RichDate {
    public static MinValue = new Date();
}

DateはTypeScriptのインターフェイスであるため、extendsキーワードを使用してクラスで拡張することはできません。これは、日付がクラスである場合に適切なソリューションになるため、少し残念です。

Dateオブジェクトを拡張MinValueしてプロトタイプのプロパティを提供する場合は、次のことができます。

interface Date {
    MinValue: Date;
}

Date.prototype.MinValue = new Date(0);

次を使用して呼び出されます:

var x = new Date();
console.log(x.MinValue);

そして、インスタンスなしでそれを利用可能にしたい場合、それもできます...しかし、それは少しうるさいです。

interface DateStatic extends Date {
    MinValue: Date;
}

Date['MinValue'] = new Date(0);

次を使用して呼び出されます:

var x: DateStatic = <any>Date; // We aren't using an instance
console.log(x.MinValue);

1
@Rajagopal明確にするために、実際にはextendsキーワードを使用してTSのインターフェースを拡張できます。クラスでインターフェースを拡張することはできません(静的プロパティを追加するために行う必要があります)。
— ジュードフィッシャー

Steve-「DateはTypeScriptのインターフェイスであるため、extendsキーワードを使用して拡張することはできません」-そうではありませんか。
— ジュードフィッシャー

1
インターフェースは拡張できますが、クラスで拡張することはできません。実装するだけです。私はそれを明確にしませんでした。
— Fenton、

私は@ニコスの意見を2番目にします。インターフェースの継承は、可読性を高めて保守性を低下させるという目的に反していませんか?新しいインターフェイスではなくインターフェイスの継承を使用する理由について合理的な意見を述べられる人はいますか?
— トーマスヘッセ

@TomasHesseその(最初の)質問に対する単一の正解はありません。これらの決定は、競合する要求間のトレードオフであり、正しい答えは適切なコンテキストでトレードオフを評価することです。
— フェントン2017年

100

@Duncanの@Bartvdsの回答に従ってください。ここでは、年月が経過した後でも実行可能な方法を提供しています。

Typescript 1.5がリリースされた(@Jun 15 '15)後のこの時点で、便利なインターフェイス

interface MyType {
    instanceMethod();
}

interface MyTypeStatic {
    new():MyType;
    staticMethod();
}

この方法は、デコレータを使用して実装できます。

/* class decorator */
function staticImplements<T>() {
    return <U extends T>(constructor: U) => {constructor};
}

@staticImplements<MyTypeStatic>()   /* this statement implements both normal interface & static interface */
class MyTypeClass { /* implements MyType { */ /* so this become optional not required */
    public static staticMethod() {}
    instanceMethod() {}
}

github open issue 13462で私のコメントを参照してください。

視覚的な結果:静的メソッドのヒントが欠けているコンパイルエラー。 ここに画像の説明を入力してください

静的メソッドが実装された後、メソッドのヒントがない。 ここに画像の説明を入力してください

静的インターフェイスと通常のインターフェイスの両方が満たされた後にコンパイルがパスしました。 ここに画像の説明を入力してください


5
あなたの答えは答えです。どうもありがとうございます。Typescript 2.4.1の操作
— Juan Camilo Guarin P

この答えは
— Typescript

2
@mmmeff 3.0.1でテストしましたが動作します。「機能しない」とはどういう意味ですか?エラーメッセージはありますか?
— Val

1
@Chklangありがとうございます!私はあなたの提案で私の答えを更新しました
— Val

1
Abstractクラスでこれを行うにはどうすればよいですか?
— geoyws

16

インターフェースは通常どおり定義できます。

interface MyInterface {
    Name:string;
}

しかし、あなたはただすることはできません

class MyClass implements MyInterface {
    static Name:string; // typescript won't care about this field
    Name:string;         // and demand this one instead
}

クラスが静的プロパティのこのインターフェイスに従う必要があることを表現するには、少しトリックが必要です。

var MyClass: MyInterface;
MyClass = class {
    static Name:string; // if the class doesn't have that field it won't compile
}

クラスの名前を保持することもできます。TypeScript(2.0)では問題ありません。

var MyClass: MyInterface;
MyClass = class MyClass {
    static Name:string; // if the class doesn't have that field it won't compile
}

多くのインターフェースを静的に継承したい場合は、まずそれらを新しいインターフェースにマージする必要があります:

interface NameInterface {
    Name:string;
}
interface AddressInterface {
    Address:string;
}
interface NameAndAddressInterface extends NameInterface, AddressInterface { }
var MyClass: NameAndAddressInterface;
MyClass = class MyClass {
    static Name:string; // if the class doesn't have that static field code won't compile
    static Address:string; // if the class doesn't have that static field code won't compile
}

または、マージされたインターフェイスに名前を付けたくない場合は、次のようにできます。

interface NameInterface {
    Name:string;
}
interface AddressInterface {
    Address:string;
}
var MyClass: NameInterface & AddressInterface;
MyClass = class MyClass {
    static Name:string; // if the class doesn't have that static field code won't compile
    static Address:string; // if the class doesn't have that static field code won't compile
}

実施例


エラーTS2322が発生する:匿名クラスを型に割り当てることができません
— JoshuaDavid 2017

@FireCodingどのTypeScriptバージョンを使用していますか?私の例は2.0で動作しました
— Kamil Szot 2017年

これは、カスタムデコレータよりも簡単なソリューションです@staticImplements。
— Robert Penner

1
これは素晴らしい解決策だと思います。使用できる非同期静的メソッドの場合reply(msg: IBotMsg): Promise<IBotReply>
— dcsan

静的メソッドとインスタンスメソッドの両方を定義する方法はありますか、それともすべてのメソッドが静的になりましたか?
— dcsan

15

静的プロパティは通常、オブジェクトの(グローバル)コンストラクターに配置されますが、「interface」キーワードはオブジェクトのインスタンスに適用されます。

TypeScriptでクラスを記述している場合、前述の答えはもちろん正しいです。他の場所に既に実装されているオブジェクトを記述している場合、静的プロパティを含むグローバルコンストラクターは次のように宣言できることを他の人が知るのに役立つかもしれません。

declare var myInterface : {
  new(): Interface;
  Name:string;
}

編集:以下の
— Bartvds

Typescript 1.5がリリースされた(6.15.2015)後のこの時点で、クラスの静的メンバーを記述するための実行可能な方法です。 stackoverflow.com/a/43674389/681830
— Val

8

静的修飾子は型メンバーに表示できません(TypeScriptエラーTS1070)。そのため、ミッションを解決するために抽象クラスを使用することをお勧めします。

例

// Interface definition
abstract class MyInterface {
  static MyName: string;
  abstract getText(): string;
}

// Interface implementation
class MyClass extends MyInterface {
  static MyName = 'TestName';
  getText(): string {
    return `This is my name static name "${MyClass.MyName}".`;
  }
}

// Test run
const test: MyInterface = new MyClass();
console.log(test.getText());

はい、それが答えです!
— vintproykt

7

上記new()の静的型を指定する@duncanのソリューションは、インターフェイスでも機能します。

interface MyType {
    instanceMethod();
}

interface MyTypeStatic {
    new():MyType;
    staticMethod();
}

これらのどれが私のクラスになりimplementますか?
— デニスプシェノフ2014

2
この時点では、インターフェースを使用して静的メンバーを記述することはできず、インスタンスメンバーのみを記述できます。したがって、この例では、クラスはMyType(のようにclass Foo implements MyType)実装します。静的インターフェースは、既存のJSコードを説明するときに、定義でのみ実際に役立ちます。
— Bartvds 14

Typescript 1.5がリリースされた(6.15.2015)後のこの時点で、クラスの静的メンバーを記述するための実行可能な方法です。 stackoverflow.com/a/43674389/681830
— Val

4

静的クラスを定義しようとしている場合(つまり、すべてのメソッド/プロパティが静的である場合)、次のようなことができます。

interface MyStaticClassInterface {
  foo():string;
}

var myStaticClass:MyStaticClassInterface = {
  foo() {
    return 'bar';
  }
};

この場合、静的な「クラス」は実際には単なるol'-js-objectであり、 MyStaticClassInterface


4

はい、可能です。これが解決策です

export interface Foo {

    test(): void;
}

export namespace Foo {

    export function statMethod(): void {
        console.log(2);
    }

}

これはすごい!まさに私が必要としていたもの
— Umar Farooq Khawaja

1
@UmarFarooqKhawajaお役に立てて嬉しいです!
— Pavel_K

これは名前空間Fooを使用してクラス定義を代用していますか?Class FooがstatMethod()を実装する場合、名前空間は現在非推奨になっているため、これは少しハックのようです...
— dcsan

@dcsanはい、少しハックです。しかし、何をすべきか?typescriptの問題の多くは、labelを使用して何年もの間開かれていwaiting more feedbackます。したがって、静的メソッドがインターフェースに追加されるまで待つか、このようにします。
— Pavel_K

しかし、私はそれを完全に取得しません..これexport namespaceは実際にクラスの実装ですか?それは物事を大きく曲げているようです。クラスで他に何が壊れているのでしょうか。
— dcsan

3

ここで言及されていない別のオプションは、静的インターフェイスを表す型で変数を定義し、それにクラス式を割り当てることです。

interface MyType {
    instanceMethod(): void;
}

interface MyTypeStatic {
    new(): MyType;
    staticMethod(): void;
}

// ok
const MyTypeClass: MyTypeStatic = class MyTypeClass {
    public static staticMethod() { }
    instanceMethod() { }
}

// error: 'instanceMethod' is missing
const MyTypeClass1: MyTypeStatic = class MyTypeClass {
    public static staticMethod() { }
}

// error: 'staticMethod' is missing
const MyTypeClass2: MyTypeStatic = class MyTypeClass {
    instanceMethod() { }
}

効果は、デコレータでの回答と同じです、デコレータのオーバーヘッドはありません

遊び場

GitHubでの関連提案/ディスカッション



2

私は特定のユースケースでこれを(デコレータなしで)行う方法を見つけました。

静的メンバーをチェックする重要な部分は、メソッドでIObjectClass使用cls: IObjectClass<T>することcreateObjectです。

//------------------------
// Library
//------------------------
interface IObject {
  id: number;
}
interface IObjectClass<T> {
  new(): T;
  table_name: string;
}
function createObject<T extends IObject>(cls: IObjectClass<T>, data:Partial<T>):T {
  let obj:T = (<any>Object).assign({},
    data,
    {
      id: 1,
      table_name: cls.table_name,
    }
  )
  return obj;
}

//------------------------
// Implementation
//------------------------
export class User implements IObject {
  static table_name: string = 'user';
  id: number;
  name: string;
}

//------------------------
// Application
//------------------------
let user = createObject(User, {name: 'Jimmy'});
console.log(user.name);

0

Typescriptのインターフェイスでは静的キーワードはサポートされていませんfunction interfaceが、静的メンバーを持つを作成することで実現できます。

次のコードFactoryでは、2つの静的メンバーserialNumberとprintSerialを持つ関数インターフェイスを作成しました。

// factory is a function interface
interface Factory<T> {
    (name: string, age: number): T;

    //staic property
    serialNumber: number;

    //static method
    printSrial: () => void;
}

class Dog {
    constructor(public name: string, public age: number) { }
}

const dogFactory: Factory<Dog> = (name, age) => {
    return new Dog(name, age);
}

// initialising static members

dogFactory.serialNumber = 1234;
dogFactory.printSrial = () => console.log(dogFactory.serialNumber);


//instance of Dog that DogFactory creates
const myDog = dogFactory("spike", 3);

//static property that returns 1234
console.log(dogFactory.serialNumber)

//static method that prints the serial 1234
dogFactory.printSrial();

0

Kamil Szotのようなソリューションを実装しましたが、望ましくない影響があります。私はこれをコメントとして投稿するのに十分な評判がないので、誰かがその解決策を試みてこれを読んだ場合に備えて、ここに投稿します。

解決策は次のとおりです。

interface MyInterface {
    Name: string;
}

const MyClass = class {
    static Name: string;
};

ただし、クラス式を使用するMyClassと、型として使用できません。私が次のようなものを書いた場合:

const myInstance: MyClass;

myInstanceがタイプanyであることが判明し、私のエディタに次のエラーが表示されます:

'MyClass' refers to a value, but is being used as a type here. Did you mean 'typeof MyClass'?ts(2749)

クラスの静的な部分のインターフェースで実現したいタイプよりも重要なタイプを失うことになります。

デコレータを使用するValのソリューションは、この落とし穴を回避します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.