こんにちは。君もそろそろ、`any`の乱用に背筋が凍る感覚を覚え、`unknown`を`narrowing`する日常に慣れてきた頃じゃないかな。中級から一段上のシニアへと駆け上がるこのフェーズで、必ず壁にぶつかるのが「型を動的に操る」という概念だ。
特に、今日話す Conditional Types(条件型)における `infer` キーワード は、TypeScriptの型システムを「静的な検査ツール」から「コンパイル時に動くメタプログラミング言語」へと変貌させる禁断の果実だ。
公式ドキュメントを読んでも「ふーん、型推論ね」で終わりがちだけど、実務の現場ではAPIのレスポンス型抽出や、複雑なステート管理の型安全性を担保するための最強の武器になる。今日は、その泥臭い実務での使い方を徹底的に叩き込んでいこう。
—
そもそも `infer` って何をやっているのか?
`infer` を一言で言えば、「条件分岐の型パターンのマッチング中に、特定の型をキャプチャして変数にブチ込むキーワード」だ。英語の “infer(推論する)” の通り、TypeScriptのコンパイラに「このパターンに一致するか?一致するなら、その中身の型を俺に教えてくれ(変数として保持させてくれ)」と命令する。
型定義の中でしか使えない(`extends` 節の中だけ)という制限はあるけれど、これを使えるようになると、既存のライブラリが提供してくれている不完全な型や、複雑にネストしたオブジェクトから、欲しい型だけをスナイパーのように正確に抜き取ることができるようになるんだ。
ブラウザやランタイムは裏側でどう処理しているのか?
ここで一つ、TypeScript初学者が勘違いしやすいポイントを整理しておこう。
「`infer` を使った高度な型推論は、ブラウザのJavaScriptのランタイムでは一切実行されていない」。
TypeScriptの型システムは、あくまでV8などのJavaScriptエンジンにコードが渡る前段階(コンパイル時 / 型チェック時)にすべて解決される。TypeScriptのコンパイラ(tsc)は、AST(抽象構文木)を舐め回しながら、`infer` が指定された箇所で「あ、このジェネリクスに渡された型は、構造的にこのパターンに合致するな。じゃあこのプレースホルダーの型は `string` だな」と逆算し、コンパイル後のJavaScriptコードには1バイトの型情報も残さずに消え去る。
つまり、どれだけ複雑な `infer` を書いても、ランタイムのパフォーマンスには1ミリも影響しない。だからこそ、俺たちは安心して型パズルに没頭していいんだ。
—
現場で即コピペして使える!実践 `infer` パターン集
理屈はこの辺にして、実務で本当によく遭遇するユースケースを見ていこう。今回は、チームメンバーに見せたら「おっ」と言わせる3つのパターンを用意した。
1. 非同期関数の戻り値型をブチ抜く (`Awaited` の自作)
実務では、他の人が書いたAPIクライアントの関数から、その中身のデータ型(Promiseが解決された後の型)だけを取り出したい場面が多々ある。標準の `Awaited
// 任意の関数型、またはPromise型から、最終的に解決される中身の型を抽出する型
type MyAwaited
? MyAwaited // 再帰的にPromiseを剥がす
: T extends (…args: any[]) => Promise
? U // 非同期関数の場合はその戻り値を抽出
: T; // どちらでもなければそのまま返す
// — 実戦での使い方 —
async function fetchUserProfile() {
return { id: 1, name: ‘Taro Frontend’, role: ‘Architect’ };
}
// 関数の戻り値(Promise<{...}>)から、中身のオブジェクト型を一発で抽出!
type UserProfile = MyAwaited
/
結果:
type UserProfile = {
id: number;
name: string;
role: string;
}
/
const user: UserProfile = {
id: 42,
name: ‘Jiro React’,
role: ‘Engineer’
};
このパターンの美しいところは、`typeof fetchUserProfile` という「値の型」から、`infer` を使って一気に中身のデータ構造を復元している点だ。APIの仕様変更でレスポンス型が変わっても、型定義を二重に管理する必要がなくなる。
2. コンポーネントのProps型を引数からハッキングする
Reactなどのコンポーネント開発で、サードパーティ製ライブラリの内部コンポーネントや、他のファイルでexportされていないコンポーネントの「Props型」だけを引っぺがしたい時はないだろうか?俺たちはこれを「Propsのハッキング」と呼んでいる。
import React from ‘react’;
// サンプルとして、あえてPropsを直接exportしていないコンポーネントを想定
const SuperComplexModal = (props: { isOpen: boolean; title: string; onClose: () => void }) => {
return null;
};
// コンポーネント型からPropsの型だけを `infer` で引き剥がすユーティリティ型
type ExtractProps
// — 実戦での使い方 —
// わざわざコンポーネント側で `export type SuperComplexModalProps = …` と書かなくても型が取れる!
type ModalProps = ExtractProps
/
結果:
type ModalProps = {
isOpen: boolean;
title: string;
onClose: () => void;
}
/
const modalSettings: ModalProps = {
isOpen: true,
title: ‘警告:anyの検知’,
onClose: () => console.log(‘closed’)
};
コンポーネントの型定義が散らかっているレガシーなコードベースにおいて、この `ExtractProps` はリファクタリングの強力な味方になる。
3. 配列やタプル型から「最初の要素」や「残りの要素」を引っこ抜く
TypeScript 4.7以降、`infer` は配列やタプル型の中でも大活躍する。例えば、関数の引数タプルの「第1引数以外」を切り出したいときなどに使うテクニックだ。
// タプル型から「最初の要素の型」を抽出する
type Head
// タプル型から「先頭以外の残りの型(Rest)」を抽出する
type Tail
// — 実戦での使い方 —
type Arguments = [string, number, boolean];
type FirstArg = Head
// 結果: string
type RemainingArgs = Tail
// 結果: [number, boolean]
これ、どこで使うの?って思ったかい?
例えば、独自のイベントエミッターや、redux-toolkitのThunk、あるいはカスタムフックの引数をラップする高階関数(HOC)を書くときに、元の関数の型安全性を完全に保ったまま引数を一部改変するラッパー関数を作るときに不可欠なテクニックなんだ。
—
シニアからのアドバイス:`infer` を使うときの心構え
`infer` は非常に強力だけど、「読みにくさ(Cognitive Load)」の代償が大きいという側面がある。
1. 過度なネストを避ける
`infer` の中でさらに `infer` を使って…とやると、コンパイルエラーが出たときに「どの条件分岐でマッチに失敗したのか」が人間には追えなくなる。複雑になりそうなら、一度型を分割して名前を付けよう。
2. チームメンバーへのドキュメント化
君が書いた高度なユーティリティ型の上には、必ず「何を入力して、何を抽出したいのか」のJSDocコメントを残してあげてほしい。フロントエンドチーム全体の生産性を上げるための型が、ブラックボックス化して誰も触れなくなったら本末転倒だからね。
Conditional Typesと `infer` をマスターすれば、TypeScriptは単なるエラーチェッカーから、君のアイデアを完璧に守ってくれる相棒に変わる。ぜひ、明日のコードから試してみてくれ!

コメント