この記事の目次
APIから受け取る値に型を付けたのに、画面を開くと名前が読めずに止まる。TypeScript unknownを入口に置くと、「まだ形を確認していない値」と「アプリで使ってよい値」をコード上で分けられます。ただし、unknownという名前を付けるだけで検証が済むわけではありません。
JavaScriptのオブジェクトとif文が読める人向けに、架空の貸出道具のデータを確認します。HTTP通信は作らず、受信済みのJSONを模した値から、名前と在庫数を取り出すところに絞ります。通信失敗の扱いはfetchの応答確認で先に分けておきます。
型を書く場所と、値を検査する場所
型注釈は、プログラムを書く段階で値の扱いを点検するためのものです。外部サービスが実際に約束どおりの値を返すかまでは保証しません。たとえば在庫数が文字列の"3"になったデータも、JSONとしては正しい状態です。
as Toolのような型アサーションは、型について開発者の判断を伝える書き方です。TypeScript公式の型アサーションの説明では、変換後のJavaScriptから取り除かれ、実行時の検査は追加されません。エラー表示を消すためだけに使うと、入口の問題を後ろへ送ってしまいます。
一方、unknownはどんな値も受け取れますが、確認せずに名前などを読み出せません。この制約を利用し、入口の関数の中で検査を済ませます。確認後は元のオブジェクトを丸ごと渡さず、必要な二項目から新しいオブジェクトを作る方針です。
受け付けるデータを先に決める
練習では次の契約にします。在庫0は正常なデータで、名前が空白だけの場合は不正です。存在しない道具を在庫0へ置き換えることもありません。
| 項目 | 受け付ける条件 | 不正な例 |
|---|---|---|
| name | 前後の空白を除いて一文字以上の文字列 | 空白だけ、数値、項目なし |
| stock | 0以上の安全な整数 | 負数、小数、文字列、Infinity |
| データ全体 | 配列やnullではないオブジェクト | null、配列、数値 |
この契約は練習用です。実際のAPIで在庫が未確定になるなら、「未確定」という別の状態を設計します。名前の最大長や商品番号の検査も、この例では省いています。二項目が通ったことを、注文や認証まで安全であるという意味に広げないでください。
unknownから必要な二項目を作り直す
空の練習フォルダでnpm init -yを実行し、npm install --save-dev --save-exact typescript@5.9.3で検証用のバージョンをそろえます。Node.jsとnpmを用意してください。例はNode.js 24系、TypeScript 5.9.3で確認する構成です。
次をtool.tsに保存します。関数の戻り値は、正しいToolか、受け付けられないことを表すnullのどちらかです。
type Tool = { name: string; stock: number };
function parseTool(value: unknown): Tool | null {
if (typeof value !== "object" || value === null || Array.isArray(value)) {
return null;
}
if (!("name" in value) || !("stock" in value)) return null;
const { name, stock } = value;
if (typeof name !== "string" || name.trim() === "") return null;
if (typeof stock !== "number" || !Number.isSafeInteger(stock) || stock < 0) {
return null;
}
return { name: name.trim(), stock };
}
const inputs: unknown[] = [
{ name: " 三脚 ", stock: 3 },
{ name: "ライト", stock: 0 },
{ name: "ライト", stock: "3" },
null,
{ stock: 3 },
{ name: " ", stock: -1 }
];
for (const input of inputs) {
const tool = parseTool(input);
console.log(tool === null ? "形式を確認してください" : `${tool.name}:${tool.stock}点`);
}
次の順序で、型検査、JavaScriptへの変換、実行を行います。strictで厳密な型検査を有効にし、型エラーがある状態では出力しない指定も付けています。
npx tsc tool.ts --strict --target ES2022 --module commonjs --noEmitOnError --outDir out
node out/tool.js
結果は「三脚:3点」「ライト:0点」、続いて同じ確認案内が四行です。型検査を通ることと、不正な入力を実行時に断れることは別の確認なので、コマンドも二段階に分けます。型検査だけ成功しても、期待する六行が出るとは限りません。
分岐の順番を省くと、確認前に止まる
最初の失敗は、nullを除く前に"name" in valueを調べることです。typeof nullはobjectなので、typeofだけでは足りません。修正ではオブジェクトであること、nullではないことを先に確認します。再実行して、nullが例外ではなく確認案内になるか照合します。
公式の型の絞り込みの説明にあるように、条件分岐の後では使える型が狭まります。この例では全体の種類、項目の存在、項目の型という順に進みます。読む前に確かめる点はfindの結果の確認とも共通しますが、ここでは外部データ全体の契約を組み立てています。
二つ目の失敗は、if (!stock)で在庫を判定することです。0も偽として扱われ、正常な在庫切れまで不正になります。修正では数値・安全な整数・0以上を別々に確認します。stockが0なら「0点」、-1や0.5なら確認案内になることを再確認してください。
三つ目は、unknownのエラーを消すためanyへ変更することです。検査なしで項目を読めるようになっても、値が正しくなったわけではありません。完成例のunknownへ戻し、別ファイルでfunction read(v: unknown) { return v.name; }を型検査すると、確認前のアクセスが拒否されることを確かめられます。この意図したエラー例は完成ファイルには追加しません。
検査の境界を増やしすぎない
この関数は、JSON由来の普通のデータをアプリ内部へ受け渡す入口です。任意のJavaScriptオブジェクトに仕込まれたgetterなどまで安全に実行できる検査ではありません。また、JSONそのものが壊れている場合は、解析の段階のエラーとして別に扱います。
同じデータを表示するたびに検査を繰り返すより、読み込んだ境界で一度確認し、内部にはToolとして渡す構成が考えられます。APIの契約が大きくなり、項目ごとのエラー説明も必要なら、スキーマ検証の仕組みを検討します。ただし型定義と検査規則が別々に変わらない運用も必要です。
不正な値を追加し、二種類の検査を照合する
15分程度を目安に、inputsへ三件を加えます。{ name: "台車", stock: 2 }は「台車:2点」、stockを0.5へ変えたものは確認案内、nameを数値へ変えたものも確認案内になるのが解答です。
さらに元のinputsの名前に付いた空白は、出力では消えていても元データには残ることを確認します。完成例は元の値を書き換えません。型エラーなし、六件と追加三件の期待結果、元データの保持を別々に記録してください。JavaScriptの無料レッスンの関数へ取り込むときも、受け付ける契約と使う処理を分けて説明できることが目標です。
TRY A LESSON
記事の内容を、課題で確かめる
記載内容へのご質問や訂正のご連絡は、hello@junkbranding.comまでお願いします。