演習 - インターフェイスとクラスでジェネリックを実装する

完了

ジェネリックは、型をコンポーネントに渡す方法にすぎません。このため、ネイティブ型をジェネリック型変数だけでなく、インターフェイス、関数、クラスにも適用できます。 このユニットでは、これらの複合型でジェネリックを使用するいくつかの方法を確認します。

インターフェイス、関数、クラスでジェネリックを使用してみることにしましょう。 すべてのサンプル コードは、基本的に同じタスクを、異なる手法を使用して実行します。

ジェネリック インターフェイスを宣言する

型の注釈を型変数に置き換えることによって、インターフェイス宣言でジェネリックを使用することができます。

  1. プレイグラウンドを開いて、既存のコードをすべて削除します。

  2. 2 つのプロパティ valuemessage を持ち、プロパティの型に 2 つのジェネリック型変数 TU を使用する、Identity という名前の単純なインターフェイスを宣言します。

    interface Identity<T, U> {
        value: T;
        message: U;
    }
    
  3. Identity インターフェイスをオブジェクト型として使用して、2 つの変数を宣言します。

    let returnNumber: Identity<number, string> = {
        value: 25,
        message: 'Hello!'
    }
    let returnString: Identity<string, number> = {
        value: 'Hello!',
        message: 25
    }
    

ジェネリック インターフェイスを関数型として宣言する

ジェネリック インターフェイスを関数型として宣言することもできます。

  1. プレイグラウンドで作業を続けます。

  2. メソッドのジェネリック シグネチャ (value: T, message: U): T を含む、ProcessIdentity という名前のジェネリック インターフェイスを宣言します。 メソッドには名前がないことに注意してください。 これにより、型シグネチャが一致する任意の関数に適用することができます。

    interface ProcessIdentity<T, U> {
        (value: T, message: U): T;
    }
    
  3. ProcessIdentity インターフェイスと同じ型シグネチャを持つ、processIdentity という名前の関数を宣言します。

    function processIdentity<T, U> (value: T, message: U) : T {
        console.log(message);
        return value
    }
    
  4. 変数の型として ProcessIdentity インターフェイスを使用する、processor という名前の関数型変数を宣言し、型 T の場合は number を、型 U の場合は string を渡します。 次に、processIdentity 関数をこれに割り当てます。 以上で、この変数を関数としてコードで使用できるようになり、TypeScript によって、この型が検証されます。

    let processor: ProcessIdentity<number, string> = processIdentity;
    let returnNumber1 = processor(100, 'Hello!');   // OK
    let returnString1 = processor('Hello!', 100);   // Type check error
    

ジェネリック インターフェイスをクラス型として宣言する

ジェネリック インターフェイスを宣言し、クラスに実装することもできます。

  1. プレイグラウンドで作業を続けます。

  2. 2 つのプロパティ valuemessage を持ち、プロパティの型に 2 つのジェネリック型変数 TU を使用する、ProcessIdentity という名前のインターフェイスを宣言します。 次に、型 T の値を返す、process という名前のメソッドのジェネリック シグネチャを追加します。

    interface ProcessIdentity<T, U> {
        value: T;
        message: U;
        process(): T;
    }
    
  3. ProcessIdentity インターフェイスを実装する、processIdentity という名前のジェネリック クラスを定義します。 ここでは、processIdentity クラスの変数の型に、X および Y という名前を付けます。 型の値はチェーンを介して伝播され、変数名は重要ではないため、インターフェイスとクラスで異なる変数名を使用することができます。

    class processIdentity<X, Y> implements ProcessIdentity<X, Y> {
        value: X;
        message: Y;
        constructor(val: X, msg: Y) {
            this.value = val;
            this.message = msg;
        }
        process() : X {
            console.log(this.message);
            return this.value
        }
    }
    
  4. 新しい変数を宣言し、それに新しい processIdentity オブジェクトを割り当てて、変数の型 XYnumberstring を渡し、引数の値として numberstring を渡します。

    let processor = new processIdentity<number, string>(100, 'Hello');
    processor.process();           // Displays 'Hello'
    processor.value = '100';       // Type check error
    

ジェネリック クラスを定義する

インターフェイスを使用せずにジェネリック クラスを宣言することもできます。 次の例では、ProcessIdentity インターフェイスを実装せずに、processIdentity をジェネリック クラスとして宣言します。

class processIdentity<T, U> {
    private _value: T;
    private _message: U;
    constructor(value: T, message: U) {
        this._value = value;
        this._message = message;
    }
    getIdentity() : T {
        console.log(this._message);
        return this._value
    }
}
let processor = new processIdentity<number, string>(100, 'Hello');
processor.getIdentity();      // Displays 'Hello'