T-CREATOR

TypeScript 型カバレッジを KPI 化:`type-coverage`でチームの型品質を可視化する

TypeScript 型カバレッジを KPI 化:`type-coverage`でチームの型品質を可視化する

TypeScript を導入したプロジェクトで、「型がちゃんと付いているか」を定量的に把握できていますか?コードレビューでは見落としがちな any 型の蔓延や、型定義の抜け漏れは、プロジェクトが大きくなるにつれて深刻な品質問題を引き起こします。

本記事では、type-coverage という npm パッケージを使って、TypeScript プロジェクトの型カバレッジを数値化し、チームの開発指標(KPI)として活用する方法を詳しく解説します。CI/CD パイプラインへの組み込みから、チーム運用まで、実践的な内容をお届けしますね。

背景

TypeScript における型の重要性

TypeScript の最大の強みは、静的型付けによるコンパイル時のエラー検出です。しかし、実際のプロジェクトでは以下のような問題が発生しがちです。

  • 納期に追われて any 型で逃げてしまう
  • 外部ライブラリの型定義が不完全で any が混入する
  • チームメンバー間で型に対する意識レベルが異なる
  • レガシーコードから段階的に TypeScript 化している途中である

これらの問題を放置すると、TypeScript を導入した意味が薄れてしまいます。

型カバレッジの概念

「型カバレッジ」とは、コードベース全体のうち、どれだけの識別子(変数、関数の引数、戻り値など)に適切な型が付与されているかを示す指標です。テストカバレッジと同様に、パーセンテージで表現されます。

以下の図は、型カバレッジの概念を示しています。

mermaidflowchart TB
  codebase["コードベース全体"]
  typed["型が付いている識別子"]
  untyped["型が付いていない識別子<br/>(any, 暗黙の any など)"]
  coverage["型カバレッジ = <br/>typed / (typed + untyped) × 100%"]

  codebase --> typed
  codebase --> untyped
  typed --> coverage
  untyped --> coverage

型カバレッジが高いほど、TypeScript の恩恵を最大限に受けられていると言えるでしょう。逆に、カバレッジが低い場合は、型安全性に問題がある可能性が高いです。

なぜ KPI 化するのか

型カバレッジを KPI(重要業績評価指標)として設定することで、以下のメリットが得られます。

#メリット説明
1可視化現状の型品質を数値で把握できる
2目標設定チーム全体で改善目標を共有できる
3継続的改善定期的な計測で品質の低下を防げる
4説得力ステークホルダーへの説明が容易になる
5モチベーション数値の向上がチームのやる気につながる

特に、リファクタリングや技術的負債の返済を進める際、経営層やプロダクトオーナーに対して「型カバレッジを 80% から 95% に向上させる」といった具体的な数値目標を示せるのは大きな強みです。

課題

型品質の測定が困難

従来、TypeScript プロジェクトで型の品質を測定するには、以下のような方法しかありませんでした。

  • コンパイラの警告・エラー数をカウントする(--strict オプション使用時)
  • コードレビューで人力チェックする
  • 定期的に全ファイルを目視確認する

しかし、これらの方法には限界があります。

#方法課題
1コンパイラのエラー数エラーがゼロでも any が大量に存在する可能性がある
2コードレビューレビュアーの負担が大きく、見落としも発生しやすい
3目視確認大規模プロジェクトでは現実的ではない

これらの課題を図で整理すると、以下のようになります。

mermaidflowchart TD
  problem["型品質の測定が困難"]

  subgraph issues["主な課題"]
    issue1["any 型の検出が難しい"]
    issue2["定量的な指標がない"]
    issue3["チーム全体での共有が困難"]
  end

  subgraph impacts["影響"]
    impact1["型安全性の低下"]
    impact2["バグの混入リスク増加"]
    impact3["リファクタリングコスト増大"]
  end

  problem --> issues
  issue1 --> impact1
  issue2 --> impact2
  issue3 --> impact3

CI/CD での自動チェックの欠如

テストカバレッジは CI/CD パイプラインで自動計測され、閾値を下回るとビルドが失敗するように設定されているプロジェクトが多いでしょう。しかし、型カバレッジについては、そのような仕組みが整備されていないケースがほとんどです。

結果として、以下のような問題が発生します。

  • プルリクエストで any 型が混入しても気づかない
  • 時間の経過とともに型カバレッジが徐々に低下していく
  • 後から型を付け直すコストが膨大になる

チーム内での認識のズレ

「型をちゃんと付ける」という基準は、人によって解釈が異なります。ある開発者は as any を多用し、別の開発者は厳密に型を定義する、といった状況では、コードベースの品質がバラバラになってしまいます。

定量的な指標がないため、チーム内で「何が良い状態か」について共通認識を持つことが難しいのです。

解決策

type-coverage の導入

type-coverage は、TypeScript プロジェクトの型カバレッジを計測できる npm パッケージです。コードベース全体をスキャンし、型が付いていない箇所を検出して、カバレッジをパーセンテージで報告してくれます。

インストール

Yarn を使ってインストールしましょう。

bashyarn add --dev type-coverage

これで、開発依存関係として type-coverage がプロジェクトに追加されます。

基本的な使い方

インストール後、以下のコマンドで型カバレッジを計測できます。

bashyarn type-coverage

実行すると、以下のような出力が得られるでしょう。

plaintext2345 / 2500 95.00%
type-coverage success.

この例では、全 2500 個の識別子のうち、2345 個に型が付いており、カバレッジは 95.00% であることを示しています。

詳細な出力オプション

どの箇所に型が付いていないかを確認するには、--detail オプションを使用します。

bashyarn type-coverage --detail

実行結果の例:

plaintextsrc/utils/helper.ts:12:7 - any
src/components/Button.tsx:45:15 - any
src/api/client.ts:78:3 - any
2345 / 2500 95.00%

これにより、具体的なファイル名、行番号、列番号が表示されるため、修正すべき箇所をピンポイントで特定できます。

型カバレッジの仕組み

type-coverage は、TypeScript Compiler API を利用してコードを解析し、各識別子の型情報を取得します。以下の図は、その処理フローを示しています。

mermaidflowchart LR
  source["ソースコード<br/>(*.ts, *.tsx)"]
  ts_api["TypeScript<br/>Compiler API"]
  analyze["型情報の解析"]
  count["型あり/型なしの<br/>カウント"]
  report["カバレッジレポート<br/>(%)"]

  source --> ts_api
  ts_api --> analyze
  analyze --> count
  count --> report

この仕組みにより、人手では困難な大規模コードベースの型チェックを、わずか数秒で完了できます。

CI/CD への組み込み

型カバレッジを KPI として活用するには、CI/CD パイプラインで自動計測する仕組みが不可欠です。

package.json へのスクリプト追加

まず、package.json に専用のスクリプトを追加しましょう。

json{
  "scripts": {
    "type-check": "tsc --noEmit",
    "type-coverage": "type-coverage --at-least 95 --detail"
  }
}

ここでは、--at-least 95 オプションを指定しています。これにより、カバレッジが 95% 未満の場合、コマンドがエラー(終了コード 1)を返すようになります。

GitHub Actions での設定例

GitHub Actions を使っている場合、以下のようなワークフローを追加します。

yamlname: Type Coverage Check

on:
  pull_request:
    branches:
      - main
  push:
    branches:
      - main

次に、ジョブの定義を追加します。

yamljobs:
  type-coverage:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

続いて、Node.js のセットアップと依存関係のインストールを行います。

yaml- name: Setup Node.js
  uses: actions/setup-node@v3
  with:
    node-version: '18'

- name: Install dependencies
  run: yarn install --frozen-lockfile

最後に、型カバレッジのチェックを実行します。

yaml- name: Check type coverage
  run: yarn type-coverage

この設定により、プルリクエストが作成されるたびに型カバレッジがチェックされ、閾値を下回るとビルドが失敗するようになります。

GitLab CI での設定例

GitLab CI を使用している場合は、.gitlab-ci.yml に以下のジョブを追加しましょう。

yamltype-coverage:
  stage: test
  image: node:18
  script:
    - yarn install --frozen-lockfile
    - yarn type-coverage
  only:
    - merge_requests
    - main

これで、マージリクエスト時に自動的に型カバレッジがチェックされます。

設定ファイルでの詳細制御

type-coverage は、プロジェクトルートに .type-coverage.json という設定ファイルを配置することで、より詳細な制御が可能です。

json{
  "atLeast": 95,
  "strict": true,
  "ignoreFiles": [
    "src/**/*.test.ts",
    "src/**/*.spec.ts",
    "src/legacy/**/*"
  ]
}

主な設定項目を以下の表にまとめます。

#設定項目説明
1atLeast最低限必要なカバレッジ(パーセント)
2strict厳密モード(暗黙の any も検出)
3ignoreFiles除外するファイルのパターン(glob 形式)
4ignoreCatchcatch 句の変数を無視するか
5ignoreUnread読み取られない変数を無視するか

strict オプションの重要性

strict: true を設定すると、暗黙的な any 型も検出対象になります。例えば、以下のようなコードです。

typescript// 暗黙的な any(関数の引数に型がない)
function greet(name) {
  console.log(`Hello, ${name}`);
}

strict モードを有効にすることで、このような見落としがちな型の欠如も検出できるようになります。

ignoreFiles の活用

テストファイルやレガシーコードなど、一時的に型カバレッジの計測から除外したいファイルがある場合、ignoreFiles を活用しましょう。ただし、除外範囲を広げすぎると本来の目的を見失うため、注意が必要です。

チーム運用での活用方法

型カバレッジを KPI として効果的に活用するには、以下のような運用ルールを設定すると良いでしょう。

段階的な目標設定

いきなり 100% を目指すのは現実的ではありません。現状のカバレッジを確認し、段階的に目標を引き上げていきます。

#フェーズ目標カバレッジ期間
1現状把握計測のみ1 ヶ月
2初期改善80% 以上2 ヶ月
3中期改善90% 以上3 ヶ月
4高品質維持95% 以上継続

このように段階を踏むことで、チームへの負担を抑えながら品質向上を実現できます。

プルリクエストでのルール化

新規コードについては、型カバレッジを下げないというルールを設定しましょう。具体的には、以下のようなチェックリストをプルリクエストテンプレートに追加します。

  • CI での型カバレッジチェックがパスしていること
  • 新規追加したコードには適切な型が付与されていること
  • やむを得ず any を使用する場合はコメントで理由を明記すること

定期的なレビュー会

月次や四半期ごとに、型カバレッジの推移をチーム全体で振り返る機会を設けるのも効果的です。カバレッジが向上した部分を称賛し、改善が必要な部分について対策を話し合いましょう。

具体例

実際のプロジェクトでの導入事例

ここでは、架空の EC サイトプロジェクトを例に、type-coverage を導入する流れを見ていきます。

プロジェクトの初期状態

プロジェクト構成は以下の通りです。

plaintextproject-root/
├── src/
│   ├── components/
│   │   ├── ProductCard.tsx
│   │   └── CartButton.tsx
│   ├── api/
│   │   └── client.ts
│   ├── utils/
│   │   └── format.ts
│   └── types/
│       └── index.ts
├── package.json
└── tsconfig.json

まず、現状の型カバレッジを計測してみましょう。

bashyarn type-coverage --detail

結果は以下の通りでした。

plaintextsrc/api/client.ts:23:7 - any
src/components/ProductCard.tsx:15:12 - any
src/utils/format.ts:8:3 - any
src/utils/format.ts:34:5 - any
185 / 230 80.43%

型カバレッジは 80.43% で、4 箇所に any 型が使われていることが判明しました。

問題箇所の特定と修正

src​/​api​/​client.ts:23:7 を確認してみます。

typescript// 修正前
export async function fetchProducts() {
  const response = await fetch('/api/products');
  const data = await response.json(); // ← ここが any
  return data;
}

response.json() の戻り値は any 型になっています。これを適切な型で定義しましょう。

typescript// 型定義の追加
interface Product {
  id: number;
  name: string;
  price: number;
  imageUrl: string;
}

次に、関数の戻り値に型を指定します。

typescript// 修正後
export async function fetchProducts(): Promise<Product[]> {
  const response = await fetch('/api/products');
  const data: Product[] = await response.json();
  return data;
}

これで、この関数に関する型の問題が解決しました。

コンポーネントの型修正

src​/​components​/​ProductCard.tsx:15:12 を確認します。

typescript// 修正前
interface ProductCardProps {
  product: any; // ← ここが any
}

export const ProductCard: React.FC<ProductCardProps> = ({
  product,
}) => {
  return (
    <div className='product-card'>
      <h3>{product.name}</h3>
      <p>{product.price}円</p>
    </div>
  );
};

product プロパティが any 型になっています。先ほど定義した Product 型を使用しましょう。

typescript// 型のインポート
import { Product } from '../types';
typescript// 修正後
interface ProductCardProps {
  product: Product;
}

export const ProductCard: React.FC<ProductCardProps> = ({
  product,
}) => {
  return (
    <div className='product-card'>
      <h3>{product.name}</h3>
      <p>{product.price}円</p>
    </div>
  );
};

これで、コンポーネントの型安全性が向上しました。

ユーティリティ関数の型修正

src​/​utils​/​format.ts の問題箇所を確認します。

typescript// 修正前
export function formatPrice(price) {
  // ← 引数の型がない(暗黙の any)
  return ${price.toLocaleString()}`;
}

引数と戻り値に適切な型を付与しましょう。

typescript// 修正後
export function formatPrice(price: number): string {
  return ${price.toLocaleString()}`;
}

これで、関数の入出力が明確になりました。

修正後のカバレッジ確認

すべての修正が完了したら、再度型カバレッジを計測します。

bashyarn type-coverage

結果は以下の通りです。

plaintext230 / 230 100.00%
type-coverage success.

見事に 100% を達成できました。この状態を維持するため、CI/CD に組み込みます。

CI/CD での継続的な監視

GitHub Actions の設定を追加し、プルリクエストごとに型カバレッジをチェックします。

yamlname: Quality Check

on:
  pull_request:
    branches:
      - main
      - develop

jobs:
  quality:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'yarn'

      - name: Install dependencies
        run: yarn install --frozen-lockfile

      - name: Type check
        run: yarn type-check

      - name: Type coverage check
        run: yarn type-coverage

この設定により、以下のフローで品質が担保されます。

mermaidflowchart TD
  pr["プルリクエスト作成"]
  ci["CI/CD トリガー"]
  deps["依存関係インストール"]
  typecheck["型チェック<br/>(tsc --noEmit)"]
  coverage["型カバレッジチェック<br/>(type-coverage)"]

  pass["チェック成功<br/>マージ可能"]
  fail["チェック失敗<br/>修正が必要"]

  pr --> ci
  ci --> deps
  deps --> typecheck
  typecheck -->|成功| coverage
  typecheck -->|失敗| fail
  coverage -->|95%以上| pass
  coverage -->|95%未満| fail

これにより、型カバレッジが低下するプルリクエストは自動的にブロックされます。

ダッシュボードでの可視化

型カバレッジの推移を可視化するため、CI/CD の実行結果をダッシュボードに表示することも有効です。GitHub Actions であれば、カバレッジバッジを README に追加できます。

まず、カバレッジ情報を JSON 形式で出力します。

bashyarn type-coverage --output-format json > coverage.json

この JSON ファイルを GitHub Actions のアーティファクトとして保存し、外部サービス(Codecov、Coveralls など)と連携することで、視覚的なレポートを生成できます。

以下は、カバレッジの推移イメージです。

mermaidflowchart LR
  week1["第1週<br/>80.4%"]
  week2["第2週<br/>85.2%"]
  week3["第3週<br/>92.7%"]
  week4["第4週<br/>95.1%"]
  week5["第5週<br/>95.8%"]

  week1 --> week2
  week2 --> week3
  week3 --> week4
  week4 --> week5

  style week5 fill:#9f9

このように、週次でカバレッジが向上している様子をチーム全体で共有できると、モチベーション向上にもつながります。

レガシーコードへの段階的適用

既存の大規模プロジェクトでいきなり高いカバレッジを達成するのは困難です。その場合、以下のような戦略が有効でしょう。

ディレクトリ単位での適用

まず、新規開発を行うディレクトリのみを対象にします。

json{
  "include": ["src/features/**/*"],
  "atLeast": 95
}

新機能開発では 95% 以上を必須とし、レガシー部分は別途計画的にリファクタリングします。

段階的な閾値引き上げ

月ごとに閾値を少しずつ引き上げる方法も効果的です。

#閾値実績
11 月80%82.3%
22 月85%87.1%
33 月90%91.5%
44 月95%95.8%

このように、無理のないペースで改善を進めることが、長期的な成功につながります。

まとめ

本記事では、type-coverage を活用して TypeScript プロジェクトの型カバレッジを KPI 化する方法を解説しました。重要なポイントを振り返りましょう。

まず、型カバレッジは TypeScript の型安全性を定量的に測定する指標であり、テストカバレッジと同様に重要です。type-coverage を使えば、コードベース全体の型付け状況を数秒で把握できます。

次に、CI/CD パイプラインに組み込むことで、プルリクエストごとに自動チェックが行われ、型品質の低下を未然に防げます。GitHub Actions や GitLab CI での設定例を参考に、ぜひ導入してみてください。

また、チーム運用では段階的な目標設定が鍵となります。いきなり 100% を目指すのではなく、現状を把握した上で、無理のないペースで改善を進めることが大切です。

型カバレッジを KPI として設定することで、チーム全体で型品質への意識が高まり、長期的にメンテナンスしやすいコードベースを構築できます。「型をちゃんと付ける」という曖昧な目標が、明確な数値目標に変わることで、開発体験も向上するでしょう。

ぜひ、あなたのプロジェクトでも type-coverage を導入して、型品質の可視化に取り組んでみてください。数ヶ月後には、カバレッジ向上の成果をチームで喜び合えるはずです。

関連リンク