zukucode
主にWEB関連の情報を技術メモとして発信しています。

TypeScript ジェネリクスで引数と戻り値の型をつなぐ

同じ処理を文字列と数値の両方に使いたいとき、引数をanyにすると戻り値の型も分からなくなります。ジェネリクスを使うと「受け取った型と返す型が同じ」という関係を表せます。

最初の値を返す関数

function first<T>(items: readonly T[]): T | undefined {
  return items[0];
}

const name = first(['ノート', 'ペン']); // string | undefined
const price = first([100, 200]);      // number | undefined

Tは関数を呼ぶときに決まる型です。ここでは引数から推論されるため、通常はfirst<string>(...)と明示する必要はありません。空の配列も渡せるので、戻り値にはundefinedを含めます。

anyとの違い

function firstAny(items: any[]): any {
  return items[0];
}

const value = firstAny([100, 200]);
value.toUpperCase(); // コンパイルできるが実行時に失敗

first<T>なら数値の結果を文字列として扱う誤りを、コンパイル時に見つけられます。

型に必要な条件を付ける

何でも受け取れる関数では、Tのプロパティを直接読めません。IDを持つ値だけに限定するなら制約を付けます。

function findById<T extends { id: string }>(
  items: readonly T[],
  id: string,
): T | undefined {
  return items.find((item) => item.id === id);
}

const products = [{ id: 'p1', name: 'ノート', price: 200 }];
const product = findById(products, 'p1');
console.log(product?.price); // 200

戻り値は元の要素の型を保つため、priceも使えます。IDを持たない配列は渡せません。

文字列専用の関数なら、引数も戻り値もstringと書く方が簡単です。複数の値の間に型の関係を保ちたいときにジェネリクスを使います。


関連記事

  • TypeScript unknown型を安全に確認して使用する

    unknownは、どのような値が入っているか分からないことを表す型です。外部から受け取ったJSONやcatchした値を、確認せず操作したくない場合に使います。anyは型チェックを無効にしますが、unk...


  • TypeScript Union型を条件分岐で安全に使い分ける

    APIの結果が成功または失敗のどちらかになる場合、両方の項目を任意にした1つの型へ詰め込むと、存在しない値を読みやすくなります。Union型なら、あり得る状態を別々に表せます。statusはどの型かを...


  • TypeScript typeとinterfaceの基本的な使い分け

    TypeScriptでは、typeとinterfaceのどちらでもオブジェクトの形を定義できます。似ている部分が多いため、最初はどちらを使えばよいか迷うことがあります。今回は基本的な書き方と、分かりや...


  • TypeScript readonly配列で意図しない変更を防ぐ

    関数へ配列を渡すと、その関数からpushやsortで元の配列を書き換えられます。読み取りだけをする関数では、引数をreadonlyにして意図を型で表します。totalの中でprices.push(40...


  • TypeScript Problem DetailsをZodで検証してAPIエラーを扱う

    fetchは、サーバーが400や500を返してもPromiseをrejectしません。そのため、HTTPステータスを確認し、レスポンスの形式を検証してから、画面で扱いやすいエラーへ変換する必要がありま...


  • TypeScript オプショナルチェーンとNull合体演算子の使い方

    APIから受け取るデータには、値が存在しない項目が含まれることがあります。TypeScriptでは、オプショナルチェーンの?.とNull合体演算子の??を使用すると、nullやundefinedを安全...


  • TypeScript async awaitで非同期処理の失敗を扱う

    APIの結果を待ってから画面に表示したいときは、asyncとawaitを使います。ただし、fetchはHTTPの404や500だけでは例外になりません。通信失敗とHTTPエラーを分けて扱います。awa...


  • TypeScript mapとfilterで配列を変換する

    mapとfilterは、配列から新しい配列を作るメソッドです。どちらも元の配列を変更しませんが、役割は異なります。要素数を保ったまま各値を変換するならmap、条件に合う要素だけへ減らすならfilter...


  • AjvでAPIのレスポンスの型チェックを行う

    TypeScript fetchの共通処理を実装するでfetchの共通処理を作成しましたが、これはAPIからのレスポンスの形式があっている前提で処理を行っています。例えば以下はAPIのレスポンスの型は...


  • React useEffectで無限ループが発生するときに確認すること

    ReactのuseEffectを利用したときに無限ループが発生してしまうことがあります。特に注意したいのが、ESLintのreact-hooks/exhaustive-depsで表示された警告をUpd...