制約付きジェネリクス

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 だけを取り出す関数を書きます。いまは制約が無いためエラーになっています。エラーメッセージを読んで、足りない条件を書き足してください。

要件

  1. 型引数に制約を付けて、数値の id を持つ型だけを受け取れるようにする
  2. 戻り値は数値の配列にする
  3. 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編集部
main.ts
main.ts
学習モード

メモ

制約付きジェネリクス

⌘S で保存