1. test 1
  2. test 2
  3. test 3
  4. test 4
  5. test 5
コース一覧
TypeScript道場 fleama開発室の10問
型ガードで絞り込め

TypeScript道場 fleama開発室の10問

TypeScript入門で身に付けた技を、商店街公式フリマアプリ「fleama」開発室の仕事で試す演習専用コースです。解説はありません。依頼と完成条件だけを読み、白紙から自分で組み立てます。手が止まったときのために、方針・使う構文・部分解の3段階のヒントを各問に用意しています。判定はコンパイラそのものです。型が通らないコードは実行される前に不合格になるので、まず tsc を黙らせてから中身を合わせることになります。基本型の注釈から始まり、関数の署名、any の撲滅、interface の設計、リテラルunion、型ガード、ジェネリクス、入口での変換、入れ子のAPIレスポンスを経て、最後は型のないモジュールを strict で通る形に組み直すところまで、10問で TypeScript の主要な道具を一巡します。1問10分から45分、全10問で約4.5時間です。TypeScript入門を終えて「読めるけれど自分では書けない」と感じている方に向いています。

1
初段
01. 赤い波線を消せ10分
02. 関数に正しい型を付けよ15分
03. このanyを撲滅せよ15分
04. Item型を設計せよ20分
2
中段
01. 不正な状態を型で封じよ25分
02. 型ガードで絞り込め25分
03. 汎用関数に型を付けよ30分
04. 型を付けて事故を防げ30分
3
師範
01. APIレスポンスに型を通せ35分
02. 総合 型のないモジュールを完全TS化45分

型ガードで絞り込め

中段 / 目安 25分

凛さんがモニターに障害報告を2件並べました。「先週、同じ事故が2回ありました。findItem は 見つからないこともある 関数です。戻り値は Item | undefined。なのに、返ってきたものをそのまま .name と触ったコードが本番に出ていました」

undefined.name は実行時に落ちます。ページが真っ白になり、ユーザーには何が起きたのかわかりません。しかし TypeScript を strict で使っているなら、この事故はコンパイルの時点で止められます。Item | undefined という型は、まさに「undefined かもしれない」と宣言しているからです。

ここで効くのが narrowing (絞り込み) です。TypeScript は if 文の条件を読んで、その先で値がどの型になっているかを勝手に狭めてくれます。if (!item) return ... と早期 return を書くと、その行より下では item は Item に確定します。undefined の可能性は、return によって既に分岐の外へ追い出されているからです。

凛さんは言います。「絞り込みは、コードを読む人へのメッセージでもあります。関数の頭で危険な入口を塞いでおけば、残りの行はもう安全な世界です。読む側は身構えなくていい」

この問題では、絞り込みを 書かないと本当に型エラーになる ことを確かめてください。strictNullChecks が効いているので、ガードを消すと「'item' は 'undefined' の可能性があります」という赤が出ます。エラーが出たら、それは失敗ではなく、コンパイラが事故を先回りしてくれた証拠です。

完成条件

describeItem という関数を1つ定義してください。

引数は2つです。1つ目 items は商品の配列、2つ目 id は探したい商品の番号です。商品1件は次の3つを持ちます。

  • id — 番号 (number)
  • name — 商品名 (string)
  • price — 価格 (number)

この形を Item という interface として定義してください。そのうえで、次の2つを作ります。

  1. findItem(items, id) — id が一致する商品を返す。見つからなければ undefined を返す。戻り値の型は Item | undefined と明記してください
  2. describeItem(items, id) — findItem を呼び、早期 return で絞り込んでから 説明文を組み立てて返す

返す文字列は次の2つの形です。

  • 見つかったとき — 深煎りブレンド 480円
  • 見つからなかったとき — 該当なし

名前と価格のあいだは半角スペース1つ、価格のうしろに 円 を付けます。

書けたら1つ実験してください。if (!item) return "該当なし"; の行を消してみると、その下の item.name が赤くなるはずです。その赤が、先週の事故そのものです。確かめたら戻してください。

進め方

白紙から書いてください。手が止まったらヒントを開けます。ヒントは3段階で、①方針、②使う構文、③部分解の順に出ます。1つも開けずに通せたら、その問題は自分のものです。

開かずに10分粘って進まないなら、①だけ開けてください。粘る時間そのものには価値がありません。価値があるのは、自分で書いた行が動いた瞬間です。

要件

  1. interface Item を定義する。持つのは id(number)・name(string)・price(number) の3つ
  2. findItem(items: Item[], id: number): Item | undefined を定義し、戻り値の型を明記する
  3. describeItem(items: Item[], id: number): string を定義する
  4. describeItem は findItem の戻り値を早期 return で絞り込んでから使うこと
  5. 見つかったときは「深煎りブレンド 480円」の形の文字列を返す
  6. 見つからなかったときは「該当なし」を返す
  7. any を使わないこと

入出力例

describeItem([{"id":1,"name":"深煎りブレンド","price":480},{"id":2,"name":"抹茶ラテ","price":580}], 1) → "深煎りブレンド 480円" describeItem([{"id":1,"name":"深煎りブレンド","price":480},{"id":2,"name":"抹茶ラテ","price":580}], 2) → "抹茶ラテ 580円" describeItem([{"id":1,"name":"深煎りブレンド","price":480},{"id":2,"name":"抹茶ラテ","price":580}], 9) → "該当なし" describeItem([], 1) → "該当なし" describeItem([{"id":7,"name":"自家製レモネード","price":560},{"id":8,"name":"チーズケーキ","price":480},{"id":9,"name":"季節のフルーツティー","price":620}], 9) → "季節のフルーツティー 620円"

ヒント

前のヒントを開くと次が開きます。開かずに解けると未開封クリアです

①方針 先に Item の形を interface で書いてしまいます。次に findItem を作り、配列から一致する1件を探します。見つからない場合があるので、戻り値の型は Item | undefined です。describeItem はその戻り値をいきなり使わず、まず「無かったとき」を片付けてから本題に入ります

ヒント 2ヒント 1 を開くと読めます
ヒント 3ヒント 2 を開くと読めます
生田 陸人
監修生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア監修者プロフィールを見る →
編集 ゆめさく編集部·公開 2026/08/09

関連レッスン

  • 汎用関数に型を付けよ

    何の配列でも扱える汎用の関数を、型引数で書きます。any に逃げずに1つの関数を全チームの道具にします。

  • 型を付けて事故を防げ

    嘘の型が付いたデータに正直な型を付け直すと、掛け算の行が赤くなります。入口で直してから通します。

  • APIレスポンスに型を通せ

    入れ子になったAPIレスポンスを、as を使わずに型の入れ子で表します。通信の向こう側まで型を届かせます。

このレッスンに出てくる用語

意味があいまいなまま進んだ語は、ここから読み直せます。

  • 関数処理に名前を付けて再利用できる単位
  • 戻り値呼び出し元への返答を表す点線矢印
  • TypeScript静的型を加えた JavaScript の上位互換
  • コンパイルソースをバイトコードへ変換する処理
  • 引数位置引数=順番で渡す。
  • 配列サイズ固定の同型データの集まり
  • interface実装を持たないメソッド契約。複数 implements 可
main.ts
学習モード
エディタを読み込んでいます

メモ

型ガードで絞り込め

⌘S で保存