制約付きジェネリクス
T は「何でもよい」ので、中で何もできない
型引数はどんな型でも受け取れます。裏を返すと、関数の中で使えるものが何もありません。
function collectIds<T>(items: T[]): number[] {
return items.map((item) => item.id);
}
// エラー Property 'id' does not exist on type 'T'.数値の配列を渡されるかもしれないのだから id があるとは限らない、という指摘です。もっともな話です。
extends で条件を付ける
型引数の後ろに extends と型を書くと、この形を満たす型だけ受け付ける、と宣言できます。これを制約と呼びます。
function collectIds<T extends { id: number }>(items: T[]): number[] {
return items.map((item) => item.id);
}これで item.id が読めるようになりました。id が number であることも保証されているので、そのまま number[] として返せます。
条件は下限であって、上限ではない
制約は満たすべき最低条件です。それ以外のプロパティが付いていてもかまいません。
const works = [
{ id: 1, title: "プロフィールサイト", published: true },
{ id: 2, title: "自己紹介カード", published: false },
];
collectIds(works); // [1, 2]条件を満たさない配列は、その場で止まります。
collectIds([{ title: "スキル一覧の表" }]);
// エラー Property 'id' is missing in type ...any との違い
items: any[] と書いても item.id は通ります。ただし通るだけで、id を持たない配列を渡しても止まりません。extends は、必要な条件だけを要求して、残りは自由なままにする書き方です。要求が具体的なぶん、Work でも Profile でも id さえあれば同じ関数が使えます。
手を動かす
演習では、id を持つオブジェクトの配列から id だけを取り出す関数を書きます。いまは制約が無いためエラーになっています。エラーメッセージを読んで、足りない条件を書き足してください。
要件
- 型引数に制約を付けて、数値の
idを持つ型だけを受け取れるようにする - 戻り値は数値の配列にする
anyは使わない
入出力例
collectIds([{"id":1,"title":"プロフィールサイト"},{"id":2,"title":"自己紹介カード"},{"id":3,"title":"スキル一覧の表"}]) → [1,2,3]
collectIds([{"id":10,"published":true,"title":"プロフィールサイト"}]) → [10]
collectIds([{"id":5},{"id":6}]) → [5,6]
collectIds([]) → []ヒント
編集 LuaGate編集部