ジェネリック型のメソッドとプロパティを使用する
型変数を使用してジェネリック コンポーネントを作成する場合、すべての型に使用できるオブジェクトのプロパティとメソッドしか使用することができません。 このため、渡される型と互換性のないパラメーター値に対して操作を実行しようしたときにエラーが発生しなくなります。
ステートメント let result: T = value + value
を identity
関数に追加すると、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
します。 次の例では、string
と number
を使用して 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'.
さらに、型を別のオブジェクトのプロパティに制約することもできます。 次の例では、extends
を keyof
演算子と組み合わせて使用します。これにより、オブジェクト型を使用して、そのキーの文字列または数値リテラルの和集合が生成されます。 これで、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
式に関する問題が発生することにお気づきでしょう。 しかし、ここでは、関数に渡すことができる型は、number
と string
のみであることがわかっています。
次の例で示すように、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
型ガードは、プリミティブ型 (string
、number
、bigint
、function
、boolean
、symbol
、object
) と未定義のチェックにのみ使用できます。 クラスの型をチェックするには、instanceof
型ガードを使用します。