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分

型を付けて事故を防げ

中段 / 目安 30分

凛さんが、いま本番で動いているコードを開きました。エラーは1つも出ていません。テストも緑です。それでも凛さんは険しい顔をしています。

「このコード、今日は動いています。明日は落ちます。売上の集計に 文字列の価格が混ざり得るからです」

見せられたのは、注文データを合計する関数でした。データの一部で価格が "1200" と文字列で入っています。JavaScript は 0 + "1200" を平気で計算し、答えは "01200" という文字列になります。エラーは出ません。間違った答えが、静かに出力されるだけです。これがいちばん怖い壊れ方です。

凛さんは続けます。「型注釈を付けてください。すると コンパイルの段階で赤が出ます。その赤は、いま画面に見えていない未来のバグです。そして——赤を見たら、直すところまでが仕事です。警報を鳴らして、鳴らしっぱなしで帰る人はいません」

型は事故を早く鳴らす警報です。警報が鳴る場所には、必ず原因があります。この問題では、まず型を付けて警報を鳴らし、次に データ側と処理側の両方を直して、最後に静かな状態でコンパイルを通してください。片方だけ直しても通りません。

直し方は2つ考えられます。データの型を「数値も文字列も来る」と正直に書いて、使う直前に数値へ変換する道。もう1つは、入口で正規化してしまい、以降は数値だけが流れる世界にする道です。この問題では後者を選んでください。凛さんの言葉では「汚れは入口で落とす。奥まで持ち込まない」です。

完成条件

注文データを受け取り、合計金額を返す関数を作ります。

受け取る1件は次の3つを持ちます。

  • name — 商品名 (string)
  • price — 価格。外から来るデータなので、数値のことも文字列のこともあります
  • count — 個数。こちらも数値のことも文字列のこともあります

この「外から来た生のデータ」の形を RawOrder という interface として定義してください。price と count は number | string になります。

そのうえで次の2つを作ります。

  1. toNumber(value: number | string): number — 入口の掃除係。数値ならそのまま、文字列なら数値に変換して返す。変換できない文字列 ("" や "unknown" など) は 0 を返す
  2. totalAmount(orders: RawOrder[]): number — toNumber を通してから 価格 × 個数 を足し上げ、合計を返す

合計は必ず number で返してください。any と as は使わないでください。

書けたら実験です。totalAmount の中で toNumber を通さず order.price * order.count と直接書いてみてください。number | string どうしは掛け算できないというエラーが出ます。それが凛さんの言う「未来のバグ」です。確かめたら戻してください。

進め方

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

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

エラーが出たら、行番号を見てください。コンパイラは犯人の場所を正確に指しています。赤は敵ではありません。本番で落ちる前に教えてくれる、いちばん早い警報です。

要件

  1. interface RawOrder を定義する。name は string、price と count は number | string
  2. toNumber(value: number | string): number を定義する
  3. toNumber は数値ならそのまま返し、文字列なら数値に変換して返す
  4. 数値に変換できない文字列は 0 を返す
  5. totalAmount(orders: RawOrder[]): number を定義する
  6. totalAmount は toNumber を通してから 価格 x 個数 を足し上げること
  7. any と as を使わないこと

入出力例

totalAmount([{"count":2,"name":"深煎りブレンド","price":480},{"count":1,"name":"抹茶ラテ","price":580}]) → 1540 totalAmount([{"count":"2","name":"深煎りブレンド","price":"480"},{"count":"3","name":"抹茶ラテ","price":580}]) → 2700 totalAmount([{"count":4,"name":"カフェオレ","price":"unknown"},{"count":"","name":"チーズケーキ","price":480}]) → 0 totalAmount([{"count":2,"name":"自家製レモネード","price":"560"},{"count":"unknown","name":"季節のフルーツティー","price":620},{"count":"1","name":"深煎りブレンド","price":"480"}]) → 1600 totalAmount([]) → 0

ヒント

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

①方針 順番が大事です。先に RawOrder を number | string で正直に書いてしまいます。すると掛け算の行が赤くなります。その赤を消すために toNumber を作る、という流れです。先に toNumber から書くと、何のために作っているのかが見えません

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

関連レッスン

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

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

  • 汎用関数に型を付けよ

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

  • 型ガードで絞り込め

    Item | undefined を早期 return で絞り込みます。ガードを消すと本当に赤くなることも自分の手で確かめます。

  • 不正な状態を型で封じよ

    商品状態を3つのリテラルの union にし、それ以外が存在できない世界にします。typo が実行前に死にます。

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

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

  • テストバグを見つける工程
  • 関数処理に名前を付けて再利用できる単位
  • JavaScriptWeb で動的な振る舞いを担う言語
  • 型注釈変数や引数に : 型 で宣言する書式
  • コンパイルソースをバイトコードへ変換する処理
  • 処理計算や代入を表す長方形
  • 正規化データの重複を減らし整合性を保つ設計手法
  • interface実装を持たないメソッド契約。複数 implements 可
main.ts
学習モード
エディタを読み込んでいます

メモ

型を付けて事故を防げ

⌘S で保存