#Generics型の制約
- Genericsの型引数が、どんな構造の引数でも受け取れてしまう状態では、型によって特定の処理を実行したい場合に暗黙的に失敗してしまいます・・
//この関数はエラー(Genericsの型が曖昧で、lengthが使えません)
function countAndDescribe<T>(element: T) {
let descriptionText = '値がありません!';
if (element.length > 0) { // Property 'length' does not exist on type 'T'.
descriptionText = '値は' + element.length + '個です';
}
return [element, descriptionText];
}
- そこで、インターフェースを用意し、インターフェースをextendsして指定したプロパティ(length)を持っていることを保証します
- これで、安全に関数を呼べることになります(^o^)
- extendsキーワードで制約を課すことで、特定の型を気にする必要はなく、特定のプロパティを持つ制約は課すことができます
interface Lengthy {
length: number;
}
// Lengthyをextendsしてlengthを持っていることを保証する
function countAndDescribe<T extends Lengthy>(element: T) { // (string | T )[] を返すと推論する
let descriptionText = '値がありません!';
if (element.length > 0) {
descriptionText = '値は' + element.length + '個です';
}
return [element, descriptionText];
}
console.log( countAndDescribe('今日もいい天気') ); //安全に関数を呼べる
// ["今日もいい天気", "値は7個です"]
//タプルで戻り値の型を指定することも可能
function countAndDescribe<T extends Lengthy>(element: T) : [ T, string ]{ //文字列とその文字の数
let descriptionText = '値がありません!';
if (element.length > 0) {
descriptionText = '値は' + element.length + '個です。';
}
return [element, descriptionText];
}
console.log(countAndDescribe(['kitten', 'pawPads']));
// [Array(2), "値は2個です。"]
// 0: (2) ["kitten", "pawPads"]
// 1: "値は2個です。"
#keyofによる制約
-
keyof
キーワードでは、オブジェクトのプロパティ名から、 ユニオン型を取得できます
// obj に keyのパラメータが存在するかどうか不明なので、エラー
function extractAndConvert( obj: object, key: string) {
return 'Value: ' + obj[key]; //オブジェクトのプロパティにアクセス
}
- そこで、
keyof
を使うことで、1個目の型のプロパティに存在するという制約を課せますU extends keyof T
- = UのプロパティにTが存在
- これで、正しい構造のオブジェクトを受け取ることを保証できます
- 存在しないプロパティにアクセスすることを防ぎます
// keyofで制約を課す
function extractAndConvert<T extends object, U extends keyof T>(
obj: T,
key: U,
) {
return 'Value: ' + obj[key]; //オブジェクトのプロパティにアクセス
}
//エラーが表示される!
//extractAndConvert({ }, 'name');
// OK
const extractAndConvertedObj = extractAndConvert({ name: 'Neko' }, 'name');
console.log(extractAndConvertedObj) // Value: Neko