ジェネリック型のメソッドとプロパティを使用する

完了

型変数を使用してジェネリック コンポーネントを作成する場合、すべての型に使用できるオブジェクトのプロパティとメソッドしか使用することができません。 このため、渡される型と互換性のないパラメーター値に対して操作を実行しようしたときにエラーが発生しなくなります。

ステートメント let result: T = value + valueidentity 関数に追加すると、TypeScript は、実行時に渡す値を認識しないため、エラー The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type (算術演算の左辺は、型 'any'、'number'、'bigint' にする必要があります) が発生します。 数値以外の値を渡した場合、式によってエラーが生成されるため、TypeScript は、コンパイル時に問題を認識します。

function identity<T, U> (value: T, message: U) : T {
    let result: T = value + value;    // Error
    console.log(message);
    return result
}

ジェネリック制約を使用して型を制限する

identity 関数は、型変数に渡すために選択した任意の型を受け入れることができます。 しかし、この場合は、使用可能なすべての型を受け入れずに、value パラメーターが受け入れることができる型を、追加操作を実行できる型の範囲に制約する必要があります。 これをジェネリック制約と呼びます。

これを行うには、型変数に応じていくつかの方法があります。 1 つの方法として、カスタムの type をタプルとして宣言し、型変数をそのカスタム型で extend します。 次の例では、stringnumber を使用して ValidTypes をタプルとして宣言します。 次に、T を新しい型で拡張します。 これで、number または string 型のみを型変数に渡すことができます。

type ValidTypes = string | number;

function identity<T extends ValidTypes, U> (value: T, message: U) : T {
    let result: T = value + value;    // Error
    console.log(message);
    return result
}

let returnNumber = identity<number, string>(100, 'Hello!');      // OK
let returnString = identity<string, string>('100', 'Hola!');     // OK
let returnBoolean = identity<boolean, string>(true, 'Bonjour!'); // Error: Type 'boolean' does not satisfy the constraint 'ValidTypes'.

さらに、型を別のオブジェクトのプロパティに制約することもできます。 次の例では、extendskeyof 演算子と組み合わせて使用します。これにより、オブジェクト型を使用して、そのキーの文字列または数値リテラルの和集合が生成されます。 これで、K extends keyof T により、キー パラメーターが pet に割り当てられた型の正しい型になることが保証されます。

function getPets<T, K extends keyof T>(pet: T, key: K) {
  return pet[key];
}

let pets1 = { cats: 4, dogs: 3, parrots: 1, fish: 6 };
let pets2 = { 1: "cats", 2: "dogs", 3: "parrots", 4: "fish"}

console.log(getPets(pets1, "fish"));  // Returns 6
console.log(getPets(pets2, "3"));     // Error

ジェネリック制約の使用の詳細については、このモジュールで後ほど説明します。

ジェネリックでの型ガードの使用

TypeScript では依然として、identity 関数の value + value 式に関する問題が発生することにお気づきでしょう。 しかし、ここでは、関数に渡すことができる型は、numberstring のみであることがわかっています。

次の例で示すように、if ブロックで typeof 型ガードを使用すると、演算を実行する前に、value パラメーターの型をチェックすることができます。 TypeScript では、if ステートメントから、ブロック内に指定された値に対して演算を実行するかどうかを判断できます。

type ValidTypes = string | number;
function identity<T extends ValidTypes, U> (value: T, message: U) {   // Return type is inferred
    let result: ValidTypes = '';
    let typeValue: string = typeof value;

    if (typeof value === 'number') {           // Is it a number?
        result = value + value;                // OK
    } else if (typeof value === 'string') {    // Is it a string?
        result = value + value;                // OK
    }

    console.log(`The message is ${message} and the function returns a ${typeValue} value of ${result}`);

    return result
}

let numberValue = identity<number, string>(100, 'Hello');
let stringValue = identity<string, string>('100', 'Hello');

console.log(numberValue);       // Returns 200
console.log(stringValue);       // Returns 100100

Note

typeof 型ガードは、プリミティブ型 (stringnumberbigintfunctionbooleansymbolobject) と未定義のチェックにのみ使用できます。 クラスの型をチェックするには、instanceof 型ガードを使用します。