【入門編】Interfaceの「構造的部分型」を逆手に取った、テスト用Mockの型安全な生成戦略 – TypeScript コア・型システムの基礎解析バイブル

こんにちは。TypeScriptの世界へようこそ。

現場でコードを書いていると、「巨大なAPIレスポンスの型」や「数十個のメソッドを持つServiceクラス」をテストのために用意しなければならない場面に直面しますよね。

「全部ダミーデータを埋めるのは面倒だな…」
「でも、部分的にしか実装していないオブジェクトを渡すと型エラーが出るし…」

そんな悩み、実はTypeScriptの「構造的部分型(Structural Subtyping)」という性質を深く理解するだけで、驚くほどスマートに解決できます。今日は、この「型安全なMock生成戦略」をマスターして、テストコードを劇的に洗練させる方法を伝授します。

—

TypeScriptの「構造的部分型」とは何か?

まず、TypeScriptの哲学を知りましょう。TypeScriptは「名前」ではなく「構造」で型を判断します。

interface User {
id: number;
name: string;
email: string;
createdAt: Date;
lastLogin: Date;
}

この巨大な `User` インターフェース、テストのたびに全プロパティを埋めるのは苦行ですよね。でも、TypeScriptの心臓部はこう考えています。

「もしそのオブジェクトが、期待されるプロパティをすべて持っているなら、たとえ余分なプロパティがあっても、それはその型として扱っていいよ」

これが構造的部分型の本質です。つまり、「必要なものさえあれば、それはその型である」と見なされるのです。

—

戦略:Partial と 型アサーションの使い分け

テスト用のMockを作る際、もっとも避けるべきなのは `any` を使って型チェックを無効化することです。これではテストの意味がありません。以下の2つのテクニックを使い分けましょう。

1. `Partial` で「必要最小限」だけ宣言する

`Partial` は、インターフェースの全メンバを「オプショナル(省略可能)」に変換するユーティリティ型です。これを使えば、必要なプロパティだけを抽出してMockを作れます。

// テストに必要なプロパティだけを抽出するMock生成関数
function createMockUser(overrides: Partial): User {
return {
id: 1,
name: ‘Test User’,
email: ‘test@example.com’,
createdAt: new Date(),
lastLogin: new Date(),
…overrides, // 指定したプロパティで上書き
} as User; // ここで型を強制する
}

2. 「型アサーション」を味方につける

`as User` と書くと「邪悪なキャスト」だと感じるかもしれませんが、テストコードにおいては「この関数が返す構造は、間違いなくUserの要件を満たしている」という開発者の宣言として機能します。

ここで重要なのは、`Partial` と `as` を組み合わせることで、「コンパイラに対して、不足しているプロパティがあることを隠蔽しつつ、利用側には完全な型を提示する」という高度な抽象化が行える点です。

—

陥りやすい罠:なぜエラーが出るのか?

初心者がよくやる間違いは、直接オブジェクトリテラルでMockを作ろうとして、TypeScriptの「余剰プロパティチェック」に引っかかるパターンです。

// エラー例:型に存在しないプロパティを含めると怒られる
const mockUser: User = {
id: 1,
name: ‘Test’,
extraField: ‘oops’ // 構造的部分型でも、直接代入時は余剰プロパティチェックが働く
};

解説:
変数の型として `User` を明示的に指定すると、TypeScriptは「厳密にその型か?」をチェックします。しかし、一度関数を通したり、`Partial` を経由したりすると、このチェックの挙動が変わります。これが構造的部分型の面白いところであり、罠でもあります。

—

実践:現場で使える「最強のMock生成パターン」

最後に、私が現場でよく使う「型安全なMock生成」の完成形を紹介します。

/

  • 巨大なInterfaceを持つオブジェクトを、必要な箇所だけオーバーライドする

/
const createMockUser = (overrides: Partial = {}): User => {
// 最小限のデフォルト値
const base: User = {
id: 0,
name: ‘Default’,
email: ‘default@example.com’,
createdAt: new Date(),
lastLogin: new Date(),
};

return { …base, …overrides };
};

// 使うときはこれだけ!
const userForTest = createMockUser({ name: ‘Admin’ });
// userForTest.id は 0、name は ‘Admin’ になる。型安全性は完璧。

このテクニックをマスターすると…

1. 型定義が変更されても安心: インターフェースに新しいプロパティが増えても、`base` を修正するだけで済むため、テスト全体が壊れません。
2. テストが読みやすい: 「何が重要なのか」が `overrides` を見るだけで一目瞭然になります。
3. リファクタリングが加速する: 型が守られている安心感があるので、自信を持ってコードを書き換えられます。

—

TypeScriptは、単なるJavaScriptの拡張ではありません。「コードの構造をどう定義し、どう守るか」という設計思想そのものです。

最初は「型エラー」が煩わしく感じるかもしれませんが、それはTypeScriptがあなたの代わりに「将来のバグ」を見つけてくれている証拠です。ここをクリアすれば、あなたはもうTypeScriptを使いこなす側の人間に一歩近づいていますよ。

また次の講義でお会いしましょう。ハッピーコーディング!

タイトルとURLをコピーしました