T-CREATOR

TypeScript プロジェクトのための Prettier・ESLint 最適設定ガイド

TypeScript プロジェクトのための Prettier・ESLint 最適設定ガイド

TypeScript プロジェクトでチーム開発を行う際、コードの品質と一貫性を保つことは非常に重要です。特に複数の開発者が関わるプロジェクトでは、コードスタイルの統一やエラーの早期発見が開発効率に大きく影響します。

この記事では、Prettier と ESLint を活用したチーム開発における運用管理について詳しく解説いたします。単なる設定方法だけでなく、実際にチームで導入する際の課題や解決策、そして継続的な運用のためのベストプラクティスをお伝えします。

チーム開発での課題と解決策

コードスタイル統一の重要性

チーム開発において、コードスタイルが統一されていないと様々な問題が発生します。実際によく見られる問題を具体例とともに見ていきましょう。

よくある問題とその影響

チームでよく発生するコードスタイルの問題は、見た目以上に深刻な影響をもたらします。

typescript// 開発者A
const userInfo = {
  name: '田中太郎',
  age: 25,
  email: 'tanaka@example.com',
};

// 開発者B
const userInfo = {
  name: '田中太郎',
  age: 25,
  email: 'tanaka@example.com',
};

このような統一されていないコードスタイルは、Git のコミット時に以下のような問題を引き起こします。

bash# Git diff での不要な変更の表示例
- const userInfo = {
-   name: "田中太郎",
+ const userInfo = {
+     name: '田中太郎',
-   email: "tanaka@example.com"
+ email: 'tanaka@example.com',

Prettier 導入による自動統一

Prettier を導入することで、これらの問題を根本的に解決できます。以下の設定ファイルをプロジェクトルートに配置しましょう。

json{
  "semi": true,
  "trailingComma": "es5",
  "singleQuote": true,
  "printWidth": 80,
  "tabWidth": 2,
  "useTabs": false,
  "endOfLine": "lf",
  "bracketSpacing": true,
  "arrowParens": "avoid"
}

この設定により、すべてのコードが統一されたスタイルで自動フォーマットされます。

開発者間のコンフリクト防止

典型的なコンフリクトシナリオ

実際のプロジェクトでよく発生するコンフリクトの例を見てみましょう。

typescript// merge conflict の例
<<<<<<< HEAD
interface UserProfile {
  id: number
  name: string
  email: string
}
=======
interface UserProfile {
  id: number;
  name: string;
  email: string;
}
>>>>>>> feature/user-profile

このようなセミコロンの有無による単純なコンフリクトも、Prettier の導入で完全に防げます。

ESLint による品質担保

ESLint を適切に設定することで、コードの品質を自動的にチェックできます。

json{
  "extends": ["@typescript-eslint/recommended", "prettier"],
  "parser": "@typescript-eslint/parser",
  "plugins": ["@typescript-eslint"],
  "rules": {
    "@typescript-eslint/no-unused-vars": "error",
    "@typescript-eslint/explicit-function-return-type": "warn",
    "@typescript-eslint/no-explicit-any": "error"
  }
}

実際のエラー検知例

以下のコードは、ESLint が検知する典型的な問題の例です。

typescript// ESLint error: '@typescript-eslint/no-unused-vars'
function calculateTotal(
  price: number,
  tax: number,
  discount: number
) {
  return price * (1 + tax); // discount が未使用
}

// ESLint error: '@typescript-eslint/no-explicit-any'
function processData(data: any) {
  // any型の使用
  return data.value;
}

これらのエラーは開発時に即座に検知され、コードレビューの負担を大幅に軽減します。

段階的導入戦略

既存プロジェクトへの導入手順

既存の TypeScript プロジェクトに Prettier と ESLint を導入する際は、段階的なアプローチが重要です。

Phase 1: 環境準備

まず、必要なパッケージをインストールします。

bash# 基本パッケージのインストール
yarn add -D prettier eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin

# Prettier と ESLint の統合パッケージ
yarn add -D eslint-config-prettier eslint-plugin-prettier

Phase 2: 基本設定の作成

設定ファイルを段階的に作成していきます。まずは最小限の設定から始めましょう。

json{
  "extends": [
    "eslint:recommended",
    "@typescript-eslint/recommended"
  ],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": 2020,
    "sourceType": "module"
  },
  "rules": {
    "@typescript-eslint/no-unused-vars": "warn"
  }
}

この段階では、厳しすぎるルールは避けて、基本的なチェックのみに留めます。

Phase 3: 段階的な適用

全ファイルに一気に適用するのではなく、モジュール単位で導入していきます。

bash# 特定のディレクトリのみをチェック
yarn eslint src/utils --ext .ts,.tsx

# 特定のファイルパターンを除外
yarn eslint src --ext .ts,.tsx --ignore-pattern "**/*.test.ts"

よくある導入時のエラーと対処法

導入時に頻発するエラーとその解決策をご紹介します。

bash# Error: Cannot read config file: .eslintrc.json
# 原因: JSONファイルの記法エラー
Error in .eslintrc.json:
Unexpected token } in JSON

# 解決策: JSONの記法を確認
{
  "extends": ["@typescript-eslint/recommended"],  // 末尾のカンマを削除
  "parser": "@typescript-eslint/parser"
}

チームメンバー教育・研修

効果的な研修プログラム

チームメンバーへの教育は、技術的な説明だけでなく、なぜこれらのツールが必要なのかという背景から説明することが重要です。

研修段階内容所要時間対象者
1Prettier・ESLint の必要性とメリット30 分全員
2基本的な設定と使い方1 時間開発者
3エラー対応とカスタマイズ1 時間リーダー
4CI/CD 統合と運用ルール30 分全員

実践的なハンズオン例

実際のエラーを体験してもらうことで、理解が深まります。

typescript// 研修用の問題コード例
function getUserData(userId) {
  // 型定義なし
  const userData = getUser(userId);
  if (userData) {
    return userData.name;
  }
  return null;
}

// ESLintが検知するエラー一覧
// 1. Parameter 'userId' implicitly has an 'any' type
// 2. Variable 'userData' implicitly has an 'any' type
// 3. Missing return type annotation

抵抗への対処法

チームメンバーからの抵抗は珍しいことではありません。よくある反対意見と対処法をまとめました。

「開発速度が落ちる」という懸念

実際のデータを示して説明することが効果的です。

bash# 導入前後の開発効率比較(例)
導入前:
- コードレビュー時間: 平均45分/PR
- バグ修正時間: 平均2時間/件
- リファクタリング: 月1回、3日間

導入後:
- コードレビュー時間: 平均25分/PR (-44%)
- バグ修正時間: 平均1.2時間/件 (-40%)
- リファクタリング: 随時、累計1日/月 (-67%)

「学習コストが高い」という懸念

段階的な学習プランを提示します。

typescript// 週1の学習スケジュール例
Week 1: 基本的なPrettierの使い方
Week 2: ESLintの基本ルール理解
Week 3: TypeScript特有のルール
Week 4: カスタマイズとチーム運用

組織横断での標準化

複数プロジェクト間での統一

大規模な組織では、複数のプロジェクトが並行して開発されています。各プロジェクトで異なる設定を使っていると、開発者の移動時に混乱が生じます。

共通設定パッケージの作成

組織全体で統一された設定を配布するため、専用の npm パッケージを作成しましょう。

json{
  "name": "@yourcompany/eslint-config-typescript",
  "version": "1.0.0",
  "main": "index.js",
  "peerDependencies": {
    "@typescript-eslint/eslint-plugin": "^5.0.0",
    "@typescript-eslint/parser": "^5.0.0",
    "eslint": "^8.0.0",
    "prettier": "^2.0.0"
  }
}

設定内容をモジュール化することで、メンテナンスが容易になります。

javascript// index.js
module.exports = {
  extends: [
    'eslint:recommended',
    '@typescript-eslint/recommended',
    'prettier',
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaVersion: 2020,
    sourceType: 'module',
    project: './tsconfig.json',
  },
  rules: {
    // 組織共通ルール
    '@typescript-eslint/no-unused-vars': 'error',
    '@typescript-eslint/explicit-function-return-type':
      'warn',
    '@typescript-eslint/no-explicit-any': 'error',
  },
};

プロジェクト固有のカスタマイズ

共通設定をベースに、プロジェクト固有の要件を追加できます。

json{
  "extends": ["@yourcompany/eslint-config-typescript"],
  "rules": {
    // プロジェクト固有のルール追加
    "@typescript-eslint/naming-convention": [
      "error",
      {
        "selector": "interface",
        "format": ["PascalCase"],
        "prefix": ["I"]
      }
    ]
  },
  "overrides": [
    {
      "files": ["**/*.test.ts"],
      "rules": {
        "@typescript-eslint/no-explicit-any": "off"
      }
    }
  ]
}

カスタムルールセット作成

業務ドメイン固有のルール

業務に特化したルールを作成することで、ドメイン固有の問題を早期に発見できます。

typescript// カスタムESLintルールの例
module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description:
        'Disallow console.log in production code',
      category: 'Best Practices',
    },
    schema: [],
  },
  create(context) {
    return {
      CallExpression(node) {
        if (
          node.callee.type === 'MemberExpression' &&
          node.callee.object.name === 'console' &&
          node.callee.property.name === 'log'
        ) {
          context.report({
            node,
            message:
              'console.log should not be used in production code',
          });
        }
      },
    };
  },
};

段階的なルール強化

チームの成熟度に応じて、ルールを段階的に強化していきます。

typescript// レベル1: 基本ルール(導入初期)
const basicRules = {
  '@typescript-eslint/no-unused-vars': 'warn',
  '@typescript-eslint/no-explicit-any': 'warn',
};

// レベル2: 中級ルール(3ヶ月後)
const intermediateRules = {
  ...basicRules,
  '@typescript-eslint/explicit-function-return-type':
    'warn',
  '@typescript-eslint/prefer-const': 'error',
};

// レベル3: 上級ルール(6ヶ月後)
const advancedRules = {
  ...intermediateRules,
  '@typescript-eslint/strict-boolean-expressions': 'error',
  '@typescript-eslint/prefer-readonly': 'error',
};

プリセット配布・管理

バージョン管理戦略

設定パッケージのバージョン管理は、セマンティックバージョニングに従って行います。

bash# メジャーバージョン: 破壊的変更
1.0.0 → 2.0.0
- 既存ルールの削除
- デフォルト設定の大幅変更

# マイナーバージョン: 新機能追加
1.0.0 → 1.1.0
- 新しいルールの追加
- 設定オプションの追加

# パッチバージョン: バグ修正
1.0.0 → 1.0.1
- ルールの微調整
- ドキュメントの修正

自動更新の仕組み

Renovate を使用して、設定パッケージの更新を自動化できます。

json{
  "extends": ["config:base"],
  "packageRules": [
    {
      "matchPackageNames": [
        "@yourcompany/eslint-config-typescript"
      ],
      "rangeStrategy": "bump",
      "automerge": true,
      "automergeType": "branch"
    }
  ]
}

運用とメンテナンス

ルール変更時の対応

影響範囲の事前調査

新しいルールを追加する前に、既存コードへの影響を調査します。

bash# 新しいルールの影響を事前チェック
yarn eslint src --rule "@typescript-eslint/prefer-const: error" --format json > impact-report.json

# 影響ファイル数の集計
cat impact-report.json | jq '.[] | select(.messages | length > 0) | .filePath' | wc -l

段階的なルール適用

大きな変更は段階的に適用します。

typescript// Step 1: 新しいファイルのみに適用
{
  "overrides": [
    {
      "files": ["src/new-features/**/*.ts"],
      "rules": {
        "@typescript-eslint/strict-boolean-expressions": "error"
      }
    }
  ]
}

// Step 2: 警告レベルで全体に適用
{
  "rules": {
    "@typescript-eslint/strict-boolean-expressions": "warn"
  }
}

// Step 3: エラーレベルに格上げ
{
  "rules": {
    "@typescript-eslint/strict-boolean-expressions": "error"
  }
}

バージョンアップ戦略

互換性の確保

ESLint や TypeScript のバージョンアップ時は、互換性を慎重に確認します。

bash# バージョンアップ前のテスト環境での確認
yarn add -D @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest

# 既存コードでのテスト実行
yarn eslint src --max-warnings 0

# TypeScript コンパイルテスト
yarn tsc --noEmit

移行計画の策定

バージョンアップに伴う移行計画を事前に策定します。

フェーズ期間作業内容担当者
準備1 週間検証環境での動作確認Tech Lead
試験導入2 週間一部プロジェクトでの試験運用各チームリーダー
全面展開1 ヶ月全プロジェクトへの適用全開発者
フォローアップ2 週間問題対応と調整Tech Lead

問題発生時のデバッグ手法

よくあるエラーとその対処法

実際のプロジェクトで頻発するエラーとその解決策をご紹介します。

bash# Error: Failed to load parser '@typescript-eslint/parser'
# 原因: パッケージの依存関係の不整合
Error: Failed to load parser '@typescript-eslint/parser' declared in '.eslintrc.json'

# 解決策
yarn remove @typescript-eslint/parser @typescript-eslint/eslint-plugin
yarn add -D @typescript-eslint/parser@^5.0.0 @typescript-eslint/eslint-plugin@^5.0.0
bash# Error: Configuration for rule is invalid
# 原因: ルール設定の記法エラー
Error: Configuration for rule "@typescript-eslint/naming-convention" is invalid:
Value should be array.

# 正しい設定例
{
  "@typescript-eslint/naming-convention": [
    "error",
    {
      "selector": "variableLike",
      "format": ["camelCase"]
    }
  ]
}

デバッグツールの活用

ESLint の詳細な動作を確認するためのデバッグオプションを活用します。

bash# 詳細なデバッグ情報の出力
DEBUG=eslint:* yarn eslint src/components

# 特定ルールの動作確認
yarn eslint src --rule "no-console: error" --format compact

効果測定と改善

コード品質指標の計測

定量的な品質指標

Prettier・ESLint 導入の効果を数値で測定します。

typescript// コード品質メトリクスの収集例
interface QualityMetrics {
  cyclomaticComplexity: number; // 循環的複雑度
  codeSmellCount: number; // コードの臭い
  duplicatedLines: number; // 重複行数
  technicalDebt: number; // 技術的負債(時間)
  testCoverage: number; // テストカバレッジ
  eslintErrors: number; // ESLintエラー数
  eslintWarnings: number; // ESLint警告数
}

SonarQube との連携

より詳細な品質分析のために、SonarQube と連携します。

yaml# .github/workflows/quality-check.yml
name: Code Quality Check
on: [push, pull_request]

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: Run ESLint
        run: yarn eslint src --format json --output-file eslint-report.json

      - name: Run SonarQube Scan
        uses: sonarqube-quality-gate-action@master
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

開発効率への影響分析

開発フローの最適化

導入前後での開発フローを比較分析します。

typescript// 開発効率指標の追跡
interface DevelopmentMetrics {
  // コードレビュー関連
  reviewTime: number; // 平均レビュー時間(分)
  reviewIterations: number; // 平均レビュー回数

  // 開発時間関連
  debugTime: number; // 平均デバッグ時間(時間)
  refactorFrequency: number; // リファクタリング頻度(回/月)

  // 品質関連
  bugReportCount: number; // バグ報告数(件/月)
  hotfixDeployments: number; // 緊急デプロイ数(回/月)
}

継続的な改善プロセス

月次でメトリクスを収集し、継続的な改善を行います。

bash# 月次品質レポートの自動生成
#!/bin/bash
MONTH=$(date +%Y-%m)
OUTPUT_DIR="reports/$MONTH"

mkdir -p "$OUTPUT_DIR"

# ESLintレポート生成
yarn eslint src --format html --output-file "$OUTPUT_DIR/eslint-report.html"

# 複雑度レポート生成
yarn complexity-report --format json --output "$OUTPUT_DIR/complexity.json" src

# カバレッジレポート生成
yarn test --coverage --coverageReporters=json --coverageDirectory="$OUTPUT_DIR"

まとめ

TypeScript プロジェクトにおける Prettier と ESLint の導入は、単なるツールの設定以上の価値をチームにもたらします。

導入による主な効果

  1. コード品質の向上: 一貫したコードスタイルと早期のエラー検知
  2. 開発効率の改善: コードレビュー時間の短縮とバグ修正コストの削減
  3. チーム協調の強化: 統一されたルールによる開発者間のコンフリクト防止
  4. 継続的な改善: 定量的な指標による客観的な品質管理

成功するための重要なポイント

段階的なアプローチ

一度にすべてを導入するのではなく、チームの習熟度に合わせて段階的に機能を追加していくことが重要です。

チーム全体のコミット

技術的な導入だけでなく、チーム全体でルールを守る文化を作ることが成功の鍵となります。

継続的な見直し

設定は一度作ったら終わりではありません。プロジェクトの成長とチームの成熟に合わせて、継続的に見直しを行いましょう。

効果的な Prettier・ESLint 運用により、より高品質で保守性の高い TypeScript アプリケーションを開発できるようになります。ぜひ皆さんのプロジェクトでも、今回ご紹介した手法を参考に導入を検討してみてください。

関連リンク