こんにちは!TypeScriptの型定義の海へようこそ。フロントエンドの現場を渡り歩いているチーフアーキテクトの私です。
「よし、TypeScriptの基礎はだいたい分かってきたぞ!」と意気込んで進んでいくと、必ずと言っていいほど目の前に立ちふさがるのが、今回のお題である「Conditional Types(条件付き型)」です。
`T extends U ? X : Y` なんて見慣れない記号が出てきた日には、「もう無理、やっぱりJavaScriptのままでいいや……」とそっとエディタを閉じたくなる気持ち、ものすごくよく分かります。私も昔はここで盛大に頭を抱えましたから。
でも、大丈夫ですよ。安心してください。
今回は、この一見難解な条件付き型を、私たちの日常にある「お買い物」や「自動販売機」の仕組みに例えて、徹底的に優しく解きほぐしていきます。肩の力を抜いて、コーヒーでも飲みながら一緒に見ていきましょう!
—
1. 条件付き型って、一体なにをやってるの?
まず、難解な記号の並びを丸暗記しようとするのはちょっと待ってください。
TypeScriptの条件付き型とは、一言で言うと「もし◯◯だったらこうする、違ったらこうする」という、型のための『条件分岐(三項演算子)』です。
JavaScriptを書くとき、私たちはこんなコードをよく書きますよね。
// JavaScriptの条件分岐の例
const isMember = true;
const discount = isMember ? “20%OFF” : “定価”;
「もし会員(`isMember`)なら `20%OFF`、そうじゃなければ `定価`」というお馴染みのあれです。
Conditional Typesは、これを「値」ではなく「型」の世界でやるための機能なんです。
基本の形はこれだけ。
T extends U ? X : Y
日本語に翻訳すると、こうなります。
「もし型 `T` が、型 `U` のグループにすっぽり収まる(部分型である)なら `X` という型にして、収まらないなら `Y` という型にしなさい!」
……これだけだと、まだちょっとピンと来ないですよね。
身近な例でイメージを掴んでみましょう。
—
2. 身近な例えでイメージする「条件付き型」
例えば、街の「自動販売機」を思い浮かべてみてください。
- ボタンを押すもの(入力:`T`)が「お札」なのか、「硬貨」なのか、「Suica」なのかによって、出てくる反応や判定(条件:`extends U`)が変わりますよね。
- お札を入れたら「投入金額が足りているかチェックするルート(`X`)」へ進み、Suicaをタッチしたなら「ICカードの残高を読み取るルート(`Y`)」へ進むはずです。
TypeScriptの条件付き型もこれと全く同じです。
「今、渡された型がどういう性質を持っているか」を自動販売機のように判定して、次に進むべき型をパッと切り替えてくれる仕組みなんです。
—
3. まずは基本のコードを書いてみよう
百聞は一見に如かず。実際にエディタで動かせるコードを見てみましょう。
ここでは、「渡された型が文字列(string)かどうか」を判定する、とってもシンプルな条件付き型を作ってみます。
以下のコードを、あなたのTypeScript環境(あるいはPlaygroundなど)にそのまま貼り付けてみてください。
// 【型定義】
// 渡された型 T が string 型の仲間かどうかを判定する型
type IsString
// 【実践:実際に判定してみる】
// ① 文字列を渡してみる
type Test1 = IsString
type Test2 = IsString<"こんにちは">; // 判定結果:「はい、文字列です!」(具体的な文字もstringの仲間です)
// ② 数字を渡してみる
type Test3 = IsString
type Test4 = IsString<100>; // 判定結果:「いいえ、文字列ではありません…」
どうでしょう? `T extends string ? … : …` の部分が、「もしstringならこっち、違ったらこっち」という分岐として綺麗に機能しているのが見えてきますよね。
TypeScriptは、私たちが書いたコードの裏側で、この判定をすごいスピードで行ってくれています。
—
4. なぜこれがWeb制作・開発ですごいの?(実用的なユースケース)
「仕組みは分かったけど、これ、実際の現場のコーディングで何の役に立つの?」
そんな疑問が湧いてくる頃だと思います。
実務でよくあるのが、「渡すデータ(引数)の形によって、返ってくるデータ(戻り値)の型をガラリと変えたい」という場面です。
例えば、APIからデータを取得する関数を考えてみましょう。
「ID(数字)」を渡したら「ユーザー単体のオブジェクト」が返ってきて、「一覧フラグ(真偽値など)」を渡したら「ユーザーの配列」が返ってくる……そんな少しわがままな関数を作りたいとき、条件付き型がもの凄い力を発揮します。
ちょっと実用的なコードを覗いてみましょう。
// ユーザー情報の型
interface User {
id: number;
name: string;
}
// 条件付き型を使ったカスタム関数(の型)のイメージ
// 引数 T が boolean(一覧フラグ)かどうかで、戻り値の型を切り替える
type ApiResponse
// この型を使った関数をシミュレートしてみましょう
function fetchUserData
// 実際の通信処理のつもり(モック)
if (isList) {
// isList が true の場合は配列を返す
return [{ id: 1, name: “Yamada” }, { id: 2, name: “Sato” }] as ApiResponse
} else {
// isList が false の場合は単体を返す
return { id: 1, name: “Yamada” } as ApiResponse
}
}
// — 使うときの振る舞い —
// A. 引数に true を渡す(一覧が欲しい!)
const resultA = fetchUserData(true);
// 💡 resultA の型は自動的に「User[](ユーザーの配列)」になります!
// resultA.name と書くと、配列なのでTypeScriptが「エラーだよ!」と怒ってくれます。
// B. 引数に false を渡す(1人だけ欲しい!)
const resultB = fetchUserData(false);
// 💡 resultB の型は自動的に「User(単体のオブジェクト)」になります!
// resultB.name と書けば、バッチリ補完が効いて気持ちよくコーディングできます。
このように、条件付き型を組み合わせることで、「人間がわざわざ手動で型を指定しなくても、状況に応じてTypeScriptが賢く型を最適化してくれる」という、極上の開発体験が手に入るのです。これが、シニアエンジニアたちがこぞって条件付き型を愛用する理由です。
—
5. つまずきやすいポイントと、そっと寄り添うアドバイス
ここまで読んで、「なるほど、便利そう!」と思ってくれたなら嬉しいのですが、実際に自分で一から書こうとすると、大体こんな壁にぶつかります。
> 「`extends` って、クラスの継承(`class A extends B`)のときにも出てきた言葉じゃん! 条件分岐の `extends` と何が違うの?」
めちゃくちゃ鋭い疑問です!
実はここ、TypeScript初学者が100%通る最初の迷い道なんですよ。
- クラスの継承における `extends`
- 「Bという設計図をベースにして、Aという新しい設計図を拡張して作るよ」という【構造の拡張】の意味。
- 条件付き型における `extends`
- 「型Tが、型Uのルール(枠組み)の中にすっぽり収まるか・包含されているか」を調べる【包含関係のチェック(アサーション)】の意味。
言葉は同じ `extends` ですが、「作っている(拡張している)」のか、それとも「審査している(包摂関係を確認している)」のかという違いがあります。
もし頭がこんがらがってしまったら、「あ、今は型がその箱に入るかどうかをチェックしてるんだな(審査中だな)」と心の中でつぶやいてみてください。それだけで、ぐっと理解しやすくなりますよ。
—
まとめ
今回は、TypeScriptの奥深い世界の入り口である「Conditional Types(条件付き型)」について、できるだけ専門用語を噛み砕いてお話ししてきました。
- `T extends U ? X : Y` は、型のための「条件分岐(三項演算子)」。
- 「もしこの型なら、こっちの型に変化させる」という自動販売機のような仕組み。
- 使いこなせるようになると、入力に応じて返却される型が劇的に賢くなり、バグの少ない安全なコードが書けるようになる。
最初は暗号のように見えていた記号も、こうして意味や役割を一つひとつ分解して眺めてみると、なんだか味方に見えてきませんか?
TypeScriptの型定義に「完璧」を最初から求める必要はありません。まずはエディタで小さくコードを書いて、「おっ、型が切り替わった!」という成功体験を少しずつ積み重ねていってくださいね。
あなたのTypeScriptライフが、今日から少しでも楽しく、快適なものになりますように。
それではまた、次のコードの海でお会いしましょう!

コメント