web-dev-qa-db-ja.com

裸の関数シグネチャと他のフィールドを使用してTypeScriptインターフェイスを実装する

このTypeScriptインターフェースを実装する(そしてTypeScriptコンパイラーを満足させる)クラスを作成するにはどうすればよいですか:

interface MyInterface {
    (): string;
    text2(content: string);
}

私はこの関連する答えを見ました: クラスがTypescriptでコール署名を実装する方法?

ただし、インターフェイスに必要な機能シグネチャのみがある場合にのみ機能します。実装する追加のメンバー(関数text2など)がある場合は機能しません。

44
user1147862

クラスは、TypeScriptインターフェイスで利用可能なすべてを実装することはできません。 2つの主な例は、呼び出し可能な署名とインデックス操作です。 : インデックス可能なインターフェイスの実装

理由は、インターフェイスは主にJavaScriptオブジェクトが実行できることを記述するために設計されているためです。したがって、非常に堅牢である必要があります。ただし、TypeScriptクラスは、プロトタイプの継承をより多くOO従来/わかりやすく/入力しやすい方法で具体的に表すように設計されています。

そのインターフェースに続くオブジェクトを作成できます:

interface MyInterface {
    (): string;
    text2(content: string);
}

var MyType = ((): MyInterface=>{
  var x:any = function():string { // Notice the any 
      return "Some string"; // Dummy implementation 
  }
  x.text2 = function(content:string){
      console.log(content); // Dummy implementation 
  }
  return x;
}
);
43
basarat

ES6のObject.assignでこれを行う簡単でタイプセーフな方法があります。

const foo: MyInterface = Object.assign(
  // Callable signature implementation
  () => 'hi',
  {
    // Additional properties
    text2(content) { /* ... */ }
  }
)

この質問が最初に質問されて回答されたときにTypeScriptで利用可能ではなかったと思う交差タイプは、入力を正しくするための秘密のソースです。

16
Tom Crockett

受け入れられた答え について詳しく説明します。

私の知る限り、呼び出し署名を実装する唯一の方法は、関数/メソッドを使用することです。残りのメンバーを実装するには、この関数でそれらを定義するだけです。これは、C#またはJavaから来ている開発者にとっては奇妙に思えるかもしれませんが、JavaScriptでは普通だと思います。

JavaScriptでは、関数を定義してメンバーを追加するだけなので、これは簡単です。ただし、この例ではFunctiontext2メンバーを定義していないため、TypeScriptの型システムはこれを許可しません。

したがって、目的の結果を得るには、関数のメンバーを定義するときに型システムをバイパスする必要があります。その後、結果をインターフェイス型にキャストできます。

//A closure is used here to encapsulate the temporary untyped variable, "result".
var implementation = (() => {
    //"any" type specified to bypass type system for next statement.
    //Defines the implementation of the call signature.
    var result: any = () => "Hello";

    //Defines the implementation of the other member.
    result.text2 = (content: string) => { };

    //Converts the temporary variable to the interface type.
    return <MyInterface>result;
})(); //Invokes the closure to produce the implementation

クロージャを使用する必要がないことに注意してください。結果のインターフェース実装と同じスコープで一時変数を宣言するだけで済みます。もう1つのオプションは、クロージャー関数に名前を付けて読みやすくすることです。

私が考えるより現実的な例は次のとおりです。

interface TextRetriever {
    (): string;
    Replace(text: string);
}

function makeInMemoryTextRetriever(initialText: string) {
    var currentText = initialText;
    var instance: any = () => currentText;
    instance.Replace = (newText: string) => currentText = newText;

    return <TextRetriever>instance;
}

var inMemoryTextRetriever = makeInMemoryTextRetriever("Hello");
9
Sam