メインコンテンツまでスキップ

第01章:DbCってなに?「約束」でバグを入口で止める🤝🛑

この章でできるようになること🎯✨

  • 「DbC(Design by Contract)」が何を守る考え方なのかを、ふんわりじゃなく説明できる🙂📘
  • 「事前条件・事後条件・不変条件」の3点セットを、コードに落とし込める🧩✅
  • 「コメントより、実行される約束」がなぜ強いのかを体感できる📝➡️💥
  • 小さな関数に“契約”を入れて、バグを入口で止められる🚪🛑✨

1. DbCは「関数の入口に置く約束」🤝🚪

DbCは、ひとことで言うと……

「この関数に入ってくる値はこうであってね」(事前条件) 「成功したなら結果はこうなるよ」(事後条件) 「このオブジェクトは常にこういう状態だよ」(不変条件)

…という “約束(契約)”を、実際に動く形で書く設計 です🤝✅

ポイントはここ👇

  • 壊れた値を中に入れない(入口で止める)🚪🛑
  • もし約束が守られてないなら、その場で分かるように落ちる(Fail Fast)💥⚡
  • 「いつ壊れた?」を「入口で壊れてた」に寄せる🔦✨

TypeScriptは「型」でかなり守れます🙂🧷 でも、型だけでは守れない“現実のルール”(範囲・桁数・整合性など)が必ず出てくるので、DbCが効いてきます💪✨

TypeScriptは「型(types)」で意図を表し、ツールでミスを早めに見つける言語です。(TypeScript)


2. DbCの3点セット🧩✅(超やさしく)

2.1 事前条件(Precondition)=入力の約束📥🚪

「この関数を呼ぶなら、これだけは守ってね!」っていう入口チェック🙂 例:

  • 割り算の分母は 0 じゃない
  • 文字列は空じゃない
  • 数は 1〜100 の範囲

入口で止めるのが基本です🚪🛑


2.2 事後条件(Postcondition)=結果の約束📤🎁

「成功して返るなら、結果はこうだよ!」っていう品質保証🙂✨ 例:

  • 戻り値は必ず正の数
  • 返す配列は必ず重複なし
  • 更新後の残高は計算と一致

2.3 不変条件(Invariant)=いつでも守るルール🧱🔒

これは「オブジェクト(やドメインの中心)が壊れないためのルール」です🧱✨ 例:

  • Money は金額が 0 以上
  • Email は “@” を含む
  • ユーザーの年齢は負にならない

不変条件は「どこかで守ってる」だと破れやすいので、1か所(生成時・更新時)に集めるのがコツです📌✨


3. 「コメント」より「実行される約束」📝➡️✅

コメントって、こうなりがちです👇😵‍💫

  • 書いた人は覚えてる
  • でも呼ぶ側は守らない
  • しかも、守られてないことに気づくのが遅い

コメントだけの世界(よくある悲劇)😇➡️😱

// b は 0 じゃないこと(← つい守られない)
export function divide(a: number, b: number) {
return a / b;
}

これ、b が 0 でも動いちゃいます。 その結果が Infinity になったり、次の計算で NaN が混ざったりして、別の場所で爆発します💥💥💥 「原因の場所」と「壊れた場所」が離れるのが最悪ポイントです😵‍💫🌀


4. ミニ例:契約がない関数がどう壊れるか😵‍💫

4.1 契約なし版(“壊れても進む”)🚧

export function discountPrice(price: number, rate: number) {
// rate は 0〜1 の想定、price は 0以上の想定…のつもり
return Math.floor(price * (1 - rate));
}

// うっかり
console.log(discountPrice(1000, 2)); // 😇 期待はしないけど…動く
console.log(discountPrice(-500, 0.2)); // 😇 動く

動いちゃうのが怖い😱 「どこで止めるべき?」が曖昧になると、壊れた値が奥へ奥へ流れ込みます🚪➡️🏃‍♀️➡️🏃‍♀️💨


5. まずは“超ミニ契約関数”を作ろう🧩🛠️

TypeScriptでDbCを始めるとき、いちばん簡単で強いのが チェック用の小さな関数(アサーション)を用意することです🙂✨

5.1 contracts.ts を作る📝

契約ラベル

// contracts.ts
export class ContractError extends Error {
constructor(message: string) {
super(message);
this.name = "ContractError";
}
}

export function requireCondition(condition: unknown, message: string): asserts condition {
if (!condition) throw new ContractError("Precondition: " + message);
}

export function ensureCondition(condition: unknown, message: string): asserts condition {
if (!condition) throw new ContractError("Postcondition: " + message);
}

export function invariantCondition(condition: unknown, message: string): asserts condition {
if (!condition) throw new ContractError("Invariant: " + message);
}

ここで大事なのは「名前」🧠✨

  • requireCondition → 入口(事前条件)
  • ensureCondition → 返す前(事後条件)
  • invariantCondition → 常に守る(不変条件)

読むだけで意図が伝わるのが最高です🙂📘✨


6. 契約あり版:入口で止める🚪🛑✨

さっきの割引を、DbCっぽくしてみます🙂

import { requireCondition, ensureCondition } from "./contracts";

export function discountPrice(price: number, rate: number) {
// ✅ 事前条件(入口)
requireCondition(Number.isFinite(price), "price は数値であること");
requireCondition(price >= 0, "price は 0以上であること");
requireCondition(Number.isFinite(rate), "rate は数値であること");
requireCondition(rate >= 0 && rate <= 1, "rate は 0〜1 の範囲であること");

const result = Math.floor(price * (1 - rate));

// ✅ 事後条件(結果)
ensureCondition(result >= 0, "割引後の価格は 0以上であること");

return result;
}

これでどうなる?🙂👇

  • rate = 2 とか来た瞬間に その場で止まる🛑💥
  • エラー文が「直し方」になってる(rate を 0〜1 にしてね)🧭✨
  • “変な値が奥へ流れない” 🚪🛡️

**バグが「入口で分かる」**って、想像以上にラクです🙂🍀


7. 手を動かす:VS Codeで最小プロジェクトを作る💻✨

7.1 TypeScriptをプロジェクトに入れる📦

mkdir dbc-chapter1
cd dbc-chapter1
npm init -y
npm i -D typescript
npx tsc --init

TypeScriptの最新版(安定版)は npm 上で 5.9.3 として配布されています。(npm)

7.2 VS Codeが使ってるTypeScriptを確認する🔎

VS Codeは、プロジェクトに入ってるTypeScriptを使う設定に切り替えできます🙂✨ コマンドパレットで “TypeScript: Select TypeScript Version” を実行して、Workspace版を選べます。(Visual Studio Code)


8. “最新事情”ちょいメモ(2026年1月)🗞️✨

  • TypeScript 5.9 では、ECMAScript提案に合わせた import defer などが入り、進化が続いてます。(TypeScript)
  • さらに将来に向けて、TypeScript 7(Project Corsa)としてコンパイラをネイティブ化(Go移植)して高速化が進行中です。VS Code向けプレビューや npm パッケージも用意されています。(Microsoft for Developers)
  • Node.js は 2026年1月時点で v24 が Active LTS(安定運用向き)として案内されています。(Node.js)

この章で覚えるべき本質はこれ👇 「型で守れる部分+実行時の契約で守る部分」を分けると、設計が一気に強くなる💪🧠✨


9. ミニクイズ🎲🙂(3問)

Q1️⃣ 事前条件はどこでチェックするのが基本?

A. 関数の最後 B. 関数の入口 C. どこでもOK

👉 答え:B 🚪✅(入口で止めると、原因が近い!)


Q2️⃣ 「コメント」より「実行される約束」が強い理由は?

A. コメントはかわいいから B. 守られてない時に“その場で”止められるから C. なんとなく

👉 答え:B 🛑💥(気づくのが早い=修正が早い!)


Q3️⃣ 不変条件が一番近いのはどれ?

A. 入力のルール B. 結果のルール C. “常に壊れないためのルール”

👉 答え:C 🧱🔒(ずっと守る)


10. 演習🧪✍️(“契約なし→あり”にする)

次の関数に契約を入れて、壊れた値を入口で止めてください🙂✨

お題:文字列を指定長に切り詰める✂️

export function clip(text: string, maxLen: number) {
// text は空じゃない想定
// maxLen は 1以上の想定
return text.length <= maxLen ? text : text.slice(0, maxLen);
}

✅ 入れてほしい契約の例

  • 事前条件:text は空じゃない(空文字は禁止にしてみよう🙂)
  • 事前条件:maxLen は 1以上、かつ整数
  • 事後条件:返る文字列の長さは maxLen 以下

ヒント:さっきの contracts.ts の requireCondition / ensureCondition を使う🧩✨


11. 章末チェックリスト✅✨

  • DbCが「壊れた値を中に入れない」考え方だと説明できる🤝🚪
  • 事前条件・事後条件・不変条件の違いが言える🧩🙂
  • 契約があると「入口で止まる」=原因が近くなると分かる🛑🔦
  • requireCondition / ensureCondition を使って小さな関数に契約を入れられる✍️✅