ジェネリックの概要

完了

TypeScript を使用して JavaScript アプリケーションをビルドする」ラーニング パスのこれまでのモジュールで、型の注釈をインターフェイス、関数、クラスに適用して、厳密に型指定されたコンポーネントを作成する方法について学習しました。 しかし、1 つだけではなく、さまざまな型で機能するコンポーネントを作成する場合はどうなるでしょうか? any 型を使用できますが、TypeScript 型チェック システムの機能を使用できなくなります。

ジェネリックは、コードベースで定義して再利用できるコード テンプレートです。 これを使用すると、関数、クラス、またはインターフェイスに対して、それらを呼び出す際に使用する型を指示することができます。 これは、引数を関数に渡すことと同じようなものですが、ジェネリックを使用すると、呼び出されたときにどの型を想定する必要があるかをコンポーネントに指示できます。

コードが次のような関数またはクラスである場合、ジェネリック関数を作成します。

  • さまざまなデータ型を使用する。
  • そのデータ型を複数の場所で使用する。

ジェネリックには、次の利点があります。

  • 型を使用する場合の柔軟性が向上します。
  • コードを再利用できます。
  • any 型を使用する必要性が低減します。

ジェネリックを使用する理由

ジェネリックを使用する理由を理解しやすくするために、例を見てみることにしましょう。

次の getArray 関数は、any 型の項目の配列を生成します。

function getArray(items : any[]) : any[] {
    return new Array().concat(items);
}

次に、getArray 関数を呼び出して numberArray 変数を宣言し、それに数字の配列を渡し、文字列の配列を使用して stringArray 変数を宣言します。 ただし、any 型が使用されているため、コードで stringnumberArray に、または numberstringArray にプッシュすることを妨げるものは何もありません。

let numberArray = getArray([5, 10, 15, 20]);
let stringArray = getArray(['Cats', 'Dogs', 'Birds']);
numberArray.push(25);                       // OK
stringArray.push('Rabbits');                // OK
numberArray.push('This is not a number');   // OK
stringArray.push(30);                       // OK
console.log(numberArray);                   // [5, 10, 15, 20, 25, "This is not a number"]
console.log(stringArray);                   // ["Cats", "Dogs", "Birds", "Rabbits", 30]

では、関数を呼び出したときに配列に格納される値の型を決定し、TypeScript に渡された値を型チェックして、その型であることを確認する必要がある場合はどうなるでしょうか? ここで役立つのがジェネリックです。

次の例では、ジェネリックを使用して getArray 関数を書き直します。 これで、関数を呼び出したときに、指定した任意の型が受け入れられるようになりました。

function getArray<T>(items : T[]) : T[] {
    return new Array<T>().concat(items);
}

ジェネリックでは、1 つ以上の型変数を山かっこ (< >) で囲んで定義して、コンポーネントに渡す 1 つまたは複数の型を指定します (型変数は、型パラメーターまたはジェネリック パラメーターとも呼ばれます)。上記の例の関数で、型変数は <T> という名前です。 T は、ジェネリックに使用される一般的な名前ですが、好きなように名前を付けることができます。

型変数を指定した後、パラメーター内の型または戻り値の型の代わりに、または型の注釈を追加する関数内の任意の場所で、それを使用することができます。

Diagram showing the getArray function with the T type variable following the function name.

型変数 T は、型の注釈が必要な場所ならどこでも使用できます。 getArray 関数では、items パラメーターの型および関数の戻り値の型を指定するために使用されます。また、項目の新しい配列を返すためにも使用されます。

関数を呼び出して、それに型を渡すには、関数名に <type> を追加します。 たとえば、getArray<number> は、number 値の配列のみを受け入れ、number 値の配列を返すように関数に指示します。 型は number として指定されているため、TypeScript は、number 値を関数に渡すことを想定し、それ以外の場合はエラーが発生します。

Note

関数を呼び出すときに型変数を省略すると、TypeScript は型を推論します。 ただし、これは、単純なデータに対してのみ機能します。 配列またはオブジェクトで渡すと、any 型が推論され、型チェックは行われません。

次の例では、numberArray および stringArray の変数宣言を更新し、目的の型を指定して関数を呼び出すことにより、TypeScript で無効な項目が配列に追加されないようにします。

let numberArray = getArray<number>([5, 10, 15, 20]);
numberArray.push(25);                      // OK
numberArray.push('This is not a number');  // Generates a compile time type check error

let stringArray = getArray<string>(['Cats', 'Dogs', 'Birds']);
stringArray.push('Rabbits');               // OK
stringArray.push(30);                      // Generates a compile time type check error

複数の型変数の使用

ジェネリック コンポーネントで使用できる型変数の数は、1 つに制限されません。

たとえば、identity 関数は、valuemessage の 2 つのパラメーターを受け入れ、value パラメーターを返します。 各パラメーターと戻り値の型に異なる型を割り当てるために、2 つのジェネリック TU を使用することができます。 変数 returnNumber は、value および message 引数の型として <number, string> を使用する identity 関数を呼び出すことによって初期化され、returnString は、<string, string> を使用して呼び出すことによって初期化され、returnBoolean は、<boolean, string> を使用して呼び出すことによって初期化されます。 これらの変数を使用する場合、TypeScript は、値の型チェックを行い、競合が発生する場合はコンパイル時エラーを返します。

function identity<T, U> (value: T, message: U) : T {
    console.log(message);
    return value
}

let returnNumber = identity<number, string>(100, 'Hello!');
let returnString = identity<string, string>('100', 'Hola!');
let returnBoolean = identity<boolean, string>(true, 'Bonjour!');

returnNumber = returnNumber * 100;   // OK
returnString = returnString * 100;   // Error: Type 'number' not assignable to type 'string'
returnBoolean = returnBoolean * 100; // Error: Type 'number' not assignable to type 'boolean'