type-coverage について
具体的な型を持つシンボルと any のシンボルの割合を測定し、経時的に追跡する TypeScript スキルです。JavaScript の移行や型安全性の維持に取り組むチームが、基準値を設けて CI での低下を検出できます。
主な機能
- 01型付きシンボルのカバレッジ測定
- 02any 型のシンボルの特定
- 03最低カバレッジ基準の設定
- 04変化の追跡と低下の防止
活用シーン
- 01JavaScript 移行中の型安全性の監視
- 02コードレビューでのカバレッジ確認
- 03基準を下回った場合の CI 失敗
- 04any 型の解消作業の優先順位付け
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill type-coverageインストール後、Claude Code で/type-coverageを入力して読み込まれたか確認します。
詳細
型安全性の後退を防ぐために型カバレッジを追跡する
概要
コードのうち、型が付いている部分と any になっている部分の割合を監視します。
型カバレッジは、実際の型が付いているシンボルと any になっているシンボルの割合を測定します。型安全性の後退を防ぐため、時間の経過に伴う変化を追跡しましょう。
このスキルを使う場面
- JavaScript から TypeScript への移行
- 型の品質を長期にわたって維持する場合
- 型安全性に関するコードレビュー
anyの削減状況を測定する場合
鉄則
What gets measured gets managed.
Track type coverage; set coverage goals.
覚えておきましょう:
anyは気づかないうちにコード全体へ広がります- カバレッジは気づかないうちに低下することがあります
- 明示的に追跡することで、品質の低下を防げます
- カバレッジ向上に向けたチーム目標を設定しましょう
型カバレッジを理解する
let x: number = 1; // x is covered (has real type)
let y: any = 2; // y is NOT covered (has any)
let z = JSON.parse('{}'); // z is NOT covered (implicit any from JSON.parse)
型カバレッジ = (カバーされているシンボル数) / (シンボルの総数)
カバレッジを測定するツール
type-coverage パッケージ
npm install -g type-coverage
type-coverage --detail
出力:
23384/24058 97.20%
--detail を指定すると、any になっているシンボルが表示されます:
src/api.ts:15:7 - data
src/utils.ts:42:3 - result
プロジェクト固有の設定
// package.json
{
"scripts": {
"type-coverage": "type-coverage --at-least 95"
}
}
カバレッジがしきい値を下回った場合に CI を失敗させます。
any の発生源
1. 明示的な any
function process(data: any) { ... } // Developer wrote any
2. 暗黙的な any(noImplicitAny がオフの場合)
function process(data) { ... } // data is implicitly any
3. ライブラリに由来する any
const data = JSON.parse(str); // Returns any
const result = $.ajax(url); // jQuery returns any
4. 連鎖する any
function getUser(): any { ... }
const user = getUser();
// ^? any - spreads from function
const name = user.name;
// ^? any - continues spreading
改善のための戦略
JSON.parse を置き換える
// Before: returns any
const data = JSON.parse(str);
// After: validate with zod
const schema = z.object({ name: z.string() });
const data = schema.parse(JSON.parse(str));
// ^? { name: string }
ライブラリの型を修正する
// Augment JSON.parse to return unknown
declare global {
interface JSON {
parse(text: string): unknown;
}
}
代わりに unknown を使う
// Before
function parse(): any { ... }
// After
function parse(): unknown { ... }
型注釈を追加する
// Before: inferred as any from library
const result = externalLib.process(data);
// After: explicitly typed
const result: ProcessedData = externalLib.process(data);
経時的な追跡
# Record in CI
echo "$(date): $(type-coverage)" >> coverage-history.txt
# Fail if coverage decreased
type-coverage --at-least $(cat .type-coverage-baseline)
チーム目標の設定
| マイルストーン | カバレッジ目標 |
|---|---|
| 移行開始時 | 50% |
| フェーズ 1 | 75% |
| フェーズ 2 | 90% |
| 安定時 | 95%+ |
コードベースの成熟に合わせて目標を引き上げましょう。
後退を防ぐ
コミット前フック
# .husky/pre-commit
type-coverage --at-least 95
CI チェック
# .github/workflows/ci.yml
- name: Type Coverage
run: npx type-coverage --at-least 95
コードレビューのチェックリスト
- 正当な理由のない新しい
any型がない - 型カバレッジが低下していない
- 外部データが検証されている
必要な any への対処
any が避けられないこともあります:
// Document why any is needed
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const legacy: any = oldSystem.getData(); // TODO: Type when migrating legacy system
コメントやチケットでこれらを追跡しましょう。
実例
// Before migration: 60% coverage
// Sources of any:
// - JSON.parse: 15%
// - Legacy API: 12%
// - Untyped dependencies: 8%
// - Explicit any: 5%
// Action plan:
// 1. Add zod validation: +10%
// 2. Type legacy API responses: +10%
// 3. Add @types packages: +8%
// 4. Remove explicit any: +5%
// After: 93% coverage
プレッシャー耐性プロトコル
1. 「100% は達成できません」
プレッシャー:「型を付けられないコードもあります」
回答: 完璧さではなく、改善を目指しましょう。追跡できるものを追跡します。
対応: 現実的な目標を設定し、必要な例外を記録します。
2. 「作業量が多すぎます」
プレッシャー:「すべての any 型を修正するには時間がかかりすぎます」
回答: 段階的に改善しましょう。まず新しい any 型を防ぎます。
対応: 段階的に基準を引き上げ、新しい any を防ぎながら、既存のものを時間をかけて修正します。
危険信号 — 作業を止めて再検討する
- カバレッジが時間の経過とともに低下している
- 正当な理由のない新しい
any型がある - 関数の戻り値から
anyが広がっている anyを多用した未テストのコードがある
よくある正当化(すべて不適切)
| 言い訳 | 実態 |
|---|---|
| 「any は 1 つだけです」 | any は広がり、1 つが多数になります |
| 「あとで修正します」 | 追跡しなければ、あとで修正されることはありません |
| 「カバレッジは十分高いです」 | 改善に合わせて、より高い目標を設定しましょう |
クイックリファレンス
# Measure coverage
npx type-coverage
# With details
npx type-coverage --detail
# Fail if below threshold
npx type-coverage --at-least 95
# In package.json
"scripts": {
"type-coverage": "type-coverage --at-least 95"
}
要点
後退を防ぐため、型カバレッジを追跡しましょう。
any 型は気づかないうちに広がります。測定しなければ、型安全性は時間とともに損なわれます。ツールでカバレッジを追跡し、目標を設定し、カバレッジが低下した場合はビルドを失敗させましょう。
参考文献
Dan Vanderkam 著『Effective TypeScript』、項目 49「型安全性の後退を防ぐために型カバレッジを追跡する」に基づいています。