こんにちは。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生成関数
function createMockUser(overrides: Partial
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
// 最小限のデフォルト値
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を使いこなす側の人間に一歩近づいていますよ。
また次の講義でお会いしましょう。ハッピーコーディング!