TypeScript:tsconfigと厳格モード
1学習の目的
- tsconfig.json で TypeScript の厳しさを制御できることを理解する。ここまで当たり前だった型チェックの多くは、設定で有効になっていたことを知る。
- 厳格モードの各項目が何を防いでいるかを理解し、プロジェクトに適した設定を選べるようになる。
2基礎解説
ここまで null の扱いや暗黙の any が厳しくチェックされてきました。これらはすべて設定で切り替えられます。設定ファイルが tsconfig.json です。
| 設定 | 防ぐもの | 例 |
|---|---|---|
| noImplicitAny | 型を書き忘れた引数 | function f(x) |
| strictNullChecks | null/undefined の見落とし | s.length(sがnullかも) |
| noUnusedLocals | 使っていない変数 | 消し忘れたコード |
| strict | 上記をまとめて有効化 | 推奨設定 |
- "strict": true が最重要。これ1つで noImplicitAny・strictNullChecks など複数の厳格チェックがまとめて有効になる。新規プロジェクトでは必ず有効にする。
- strictNullChecks を切ると、TS06・TS08で学んだ null 安全が失われる。const s: string = null; が通るようになり、実行時エラーの温床になる。
- noImplicitAny は「型の書き忘れ」を防ぐ(TS05)。これが無いと、引数が黙って any になり、型チェックが素通りする。
- target は出力するJavaScriptのバージョン。"ES2020" なら最近の書き方がそのまま出力され、古い指定なら変換される。
- 既存プロジェクトへの導入は段階的に。いきなり strict にすると大量のエラーが出るので、1つずつ有効化して直していくのが現実的。
現場使用例:プロジェクト立ち上げ時の設定、既存コードのTypeScript化、チームでの品質基準の統一。設定はチーム全員に等しく効く「自動レビュー」になる。
// tsconfig.json の基本形
{
"compilerOptions": {
"target": "ES2020", // 出力するJSのバージョン
"module": "commonjs", // モジュールの形式
"strict": true, // ⭐ 厳格チェックをまとめて有効化
"noUnusedLocals": true, // 未使用の変数を検出
"noImplicitReturns": true, // return漏れを検出
"outDir": "./dist" // 出力先
},
"include": ["src/**/*"] // 対象のファイル
}
// ▼ strict が無効だと、これらが全部通ってしまう
// noImplicitAny が無効なら…
function bad(x) { // x は暗黙の any
return x.foo.bar; // 何も警告されない
}
// strictNullChecks が無効なら…
const s: string = null; // 通ってしまう
console.log(s.length); // 実行時に落ちる
// ✅ strict: true なら、どちらも書いた時点でエラーになる
3基本ドリル(10問)
A. package.json B. tsconfig.json C. config.ts D. tsc.json 選択★☆☆無料
解答 == B
TypeScript の設定を意味するファイル名。
B
プロジェクトのルートに置く。このファイルがあると、tsc コマンドが自動的に設定を読み込む。
A. 実行速度が上がる B. 複数の厳格チェックがまとめて有効になる C. エラーを無視する D. 型注釈が不要になる 選択★☆☆無料
解答 == B
strict は複数の設定をまとめたもの。
B
noImplicitAny・strictNullChecks など7つ以上の設定が一度に有効になる。個別に書く必要がなく、新規プロジェクトではこれ1つで十分。
出力 == 42
引数の型を省略すると noImplicitAny でエラーになる。
function double(n: number): number {
return n * 2;
}
console.log(double(21));
引数の型を書くのは noImplicitAny のため(TS05)。この設定を切れば省略できるが、型チェックの意味が失われる。
A. エラーになる B. 通ってしまう C. 空文字になる D. undefined になる 選択★☆☆無料
解答 == B
この設定が null と型を区別している。
B
TS06・TS08で学んだ null 安全がすべて失われる。s.length が実行時に落ちるまで気づけなくなる。
A. 対象のファイル B. 出力するJavaScriptのバージョン C. エラーの厳しさ D. 出力先フォルダ 選択★☆☆無料
解答 == B
変換先のJavaScriptに関する設定。
B
古いブラウザ向けなら ES5、モダンな環境なら ES2020 以上を指定する。新しい書き方が使えるかどうかに影響する。
出力 == 該当なし
戻り値の型を string | null にし、?? で既定値を指定する。
function find(id: number): string | null {
return id === 1 ? "ノートPC" : null;
}
console.log(find(99) ?? "該当なし");
strictNullChecks があるから、この確認を強制される。無効なら find(99).length と書けてしまい、実行時に落ちる。
A. 型の間違い B. 使われていない変数 C. null の見落とし D. return の書き忘れ 選択★★☆無料
解答 == B
unused は「使われていない」という意味。
B
消し忘れたコードや、書きかけの変数を見つけてくれる。コードの掃除に役立つが、開発中は煩わしいこともあるので、プロジェクトによって判断が分かれる。
A. いきなり strict を有効にする B. 設定を1つずつ有効にして段階的に直す C. すべて any にする D. 型を一切書かない 選択★★☆無料
解答 == B
いきなり全部有効にすると何が起きるか。
B
いきなり strict にすると数千件のエラーが出ることもある。noImplicitAny から始めて1つずつ潰していくのが現実的な移行手順。
出力2行。合格 / 不合格
if の中だけで return すると、else の経路で return が無くなる。
function judge(score: number): string {
if (score >= 60) {
return "合格";
}
return "不合格";
}
console.log(judge(80));
console.log(judge(50));
すべての経路で値を返している。if の中だけに return を書くと、条件を満たさない場合に undefined が返り、TS04で学んだエラーになる。
出力 == 処理に失敗しました
catch (e) の e は unknown。instanceof Error で確認する(TS14)。
try {
throw new Error("処理に失敗しました");
} catch (e) {
console.log(e instanceof Error ? e.message : "不明なエラー");
}
useUnknownInCatchVariables という設定によるもの。strict に含まれており、これが無ければ e は any になって確認せずに使えてしまう。
4実践シナリオ(5問)
仕様:価格(number)と割引率(number | null)を受け取り、割引率が null なら定価、そうでなければ割引後の価格(四捨五入)を返す。20000円で割引率0.1と null の2通りを試すこと。 コーディング★★☆無料
出力2行。18000 / 20000
割引率が null かどうかを確認してから計算する。
function calcTotal(price: number, rate: number | null): number {
if (rate === null) {
return price;
}
return Math.round(price * (1 - rate));
}
console.log(calcTotal(20000, 0.1));
console.log(calcTotal(20000, null));
3つの厳格チェックをすべて満たしている。引数に型があり、両方の経路で return し、null を確認してから計算に使っている。
出力2行が完全一致
find の結果は undefined になりうる。?? や条件分岐で対処する。
type Product = { name: string; price: number };
const items: Product[] = [{ name: "ノートPC", price: 128000 }];
function describe(name: string): string {
const found = items.find((i) => i.name === name);
if (!found) {
return "該当なし";
}
return `${found.name}:${found.price.toLocaleString()}円`;
}
console.log(describe("ノートPC"));
console.log(describe("存在しない商品"));
strictNullChecks があるので、found の確認を省略できない。この設定が無ければ found.name と直接書けてしまい、実行時に落ちる。
出力 == 3件・合計139,700円
引数の型は Product[]、戻り値は string。reduce のコールバックの引数は推論されるので省略可。
type Product = { name: string; price: number };
function summarize(items: Product[]): string {
const total: number = items.reduce((sum, i) => sum + i.price, 0);
return `${items.length}件・合計${total.toLocaleString()}円`;
}
const products: Product[] = [
{ name: "ノートPC", price: 128000 },
{ name: "マウス", price: 3200 },
{ name: "キーボード", price: 8500 },
];
console.log(summarize(products));
reduce の中の sum と i は推論されるので型注釈は不要。関数の入口と出口だけ型を明示すれば、内部は推論に任せられる(TS05)。
出力2行。ノートPC / 不正なデータ
TS15で学んだ型ガードを使う。any を1つも使わずに書く。
type Product = { name: string; price: number };
function parse(v: unknown): Product | null {
if (typeof v !== "object" || v === null) {
return null;
}
const o = v as Record<string, unknown>;
if (typeof o.name !== "string" || typeof o.price !== "number") {
return null;
}
return { name: o.name, price: o.price };
}
for (const input of [{ name: "ノートPC", price: 128000 }, { name: 123 }]) {
const p = parse(input);
console.log(p ? p.name : "不正なデータ");
}
any を1つも使わずに外部データを扱えている。厳格モードは「any を書かせない」ための設定であり、代わりに unknown と型ガードで安全性を担保する。
出力3行。発送済 / 配達完了 / 完了
switch で分岐し、default に never チェックを入れる(TS16)。
type Status = "未発送" | "発送済" | "配達完了";
function nextStatus(current: Status): string {
switch (current) {
case "未発送":
return "発送済";
case "発送済":
return "配達完了";
case "配達完了":
return "完了";
default: {
const exhaustive: never = current;
return exhaustive;
}
}
}
for (const s of ["未発送", "発送済", "配達完了"] as Status[]) {
console.log(nextStatus(s));
}
noImplicitReturns と never チェックが両方効いている。すべての経路で return し、かつすべてのステータスを処理していることが、二重に保証されている。
5仕上げ課題
満たすべき条件
① すべての関数の引数に型注釈がある(noImplicitAny)
② null / undefined の可能性を必ず確認する(strictNullChecks)
③ すべての分岐で return する(noImplicitReturns)
④ catch のエラーは型を確認してから使う
⑤ any を1つも使わない
実装する内容
・型:Item = { id: number; name: string; stock: number | null }
・stockLabel(item) … 在庫がnullなら「未登録」、0なら「品切れ」、10未満なら「残りわずか」、それ以外は「在庫あり」
・findItem(id) … 見つからなければ undefined を返す
・reduceStock(id, qty) … 在庫を減らす。商品が無い/在庫未登録/在庫不足なら例外を投げる
データ:ノートPC(id:1,在庫5)、マウス(id:2,在庫null)、サンプル(id:3,在庫0)
期待される出力(6行)
ノートPC:残りわずか
マウス:未登録
サンプル:品切れ
---
減算成功:残り3個
エラー:在庫が未登録です
出力6行が完全一致
stockLabel は null チェックを最初に行う。reduceStock は3つの失敗パターンで例外を投げ、catch では instanceof Error で確認する。ノートPCから2個減らし、マウス(在庫null)から1個減らそうとして失敗させる。
type Item = {
id: number;
name: string;
stock: number | null;
};
const items: Item[] = [
{ id: 1, name: "ノートPC", stock: 5 },
{ id: 2, name: "マウス", stock: null },
{ id: 3, name: "サンプル", stock: 0 },
];
function stockLabel(item: Item): string {
if (item.stock === null) {
return "未登録";
}
if (item.stock === 0) {
return "品切れ";
}
if (item.stock < 10) {
return "残りわずか";
}
return "在庫あり";
}
function findItem(id: number): Item | undefined {
return items.find((i) => i.id === id);
}
function reduceStock(id: number, qty: number): number {
const item = findItem(id);
if (!item) {
throw new Error("商品が見つかりません");
}
if (item.stock === null) {
throw new Error("在庫が未登録です");
}
if (item.stock < qty) {
throw new Error("在庫が不足しています");
}
item.stock -= qty;
return item.stock;
}
for (const item of items) {
console.log(`${item.name}:${stockLabel(item)}`);
}
console.log("---");
try {
const remaining = reduceStock(1, 2);
console.log(`減算成功:残り${remaining}個`);
} catch (e) {
console.log(`エラー:${e instanceof Error ? e.message : "不明"}`);
}
try {
reduceStock(2, 1);
console.log("減算成功");
} catch (e) {
console.log(`エラー:${e instanceof Error ? e.message : "不明"}`);
}
このコードは、厳格モードのすべての制約を満たしている。そして重要なのは、制約を守ることが、そのまま堅牢なコードになっていることだ。
stock: number | null という型が、「在庫が未登録の商品がある」という業務上の現実を表している。strictNullChecks があるおかげで、item.stock - qty と直接書けば止められる。「未登録」と「0個」を混同するバグが、構造的に起きない。
findItem が Item | undefined を返すのも同じだ。if (!item) の確認を書かなければコンパイルが通らないので、「見つからなかった場合」を考え忘れることがない。
catch (e) の e instanceof Error も、strict に含まれる設定によるものだ。JavaScriptでは文字列でも数値でも throw できるため、Error だと決めつけてはいけない。
厳格モードは「面倒な制約」ではなく、「バグを事前に潰す仕組み」だ。設定を1行書くだけで、チーム全員のコードに同じ品質基準が適用される。人間のレビューでは見落とすものを、機械が確実に指摘してくれる。
次章はいよいよ最終回。ここまでの17章すべてを統合して、型安全な在庫管理システムを完成させる。