【ITニュース解説】Sonar Duplication in TypeScript Static Sites
2026年10月10日に「Dev.to」が公開したITニュース「Sonar Duplication in TypeScript Static Sites」について初心者にもわかりやすく解説しています。
ITニュース概要
TypeScriptの静的サイトで大量のデータをTypeScriptファイルに書くと、SonarQubeが重複コードとして誤検知する場合がある。これを避けるには、データをJSONファイルに分離し、TypeScriptで型安全にインポート・バリデーションするのが有効だ。これにより、コード品質測定の正確性を保ちつつ、型安全な開発を実現できる。
ITニュース解説
静的サイトは、事前に作成されたコンテンツをユーザーに提供するウェブサイトだが、規模が大きくなるにつれて、多くの設定データ、地域ごとの情報、表示コンテンツなどが蓄積される。これらのデータは、アプリケーションの振る舞いを定義するコードと一緒に、TypeScriptファイル内に直接記述されることがある。ここで問題となるのが、SonarQubeのような「コード分析ツール」が、コードの品質を評価する際に「コードの重複」を検出することだ。同じような構造や情報が繰り返し現れると、保守が難しくなったり、バグの原因になったりする可能性があるため、ツールはこれらを警告する。
もし、大量の設定データがTypeScriptファイルの中に直接記述されていると、SonarQubeはそれらをコードの一部として認識し、データの中に繰り返し構造があれば、それをコードの重複として報告する。開発者たちは、繰り返しデータをTypeScriptファイルからJSONのような専用データ形式のファイルに移した場合、SonarQubeの重複検出の指標がどうなるのか疑問に思う。単に重複がレポートから隠れるだけなのか、それとも重複が解消されるのか。
SonarQubeは、分析スコープに含まれるファイル内のコード重複を厳密に評価する。TypeScriptファイル内のデータはスキャンされ、重複が測定されるが、もしその同じデータをJSONファイルに変換し、SonarQubeの設定でそのJSONファイルを分析対象から除外した場合、そのデータに関する重複指標はゼロになる。これは、重複が本当に解消されたわけではなく、単にSonarQubeがそのデータを分析しなくなったためである。
このため、開発者は「重複ゼロ」という結果が何を意味するのかを正しく理解する必要がある。メインブランチでのコード分析結果が重複ゼロであっても、それは「分析対象となったTypeScriptやJavaScriptのコードの中に計測可能な重複がない」ことを意味するだけで、「リポジトリ全体に全く同じテキストやデータが存在しない」ことを保証するものではない。この違いを理解しないと、ローカル環境でのファイル内容と、CIツールで行われた認証済みのスキャン結果との間に混乱が生じる可能性がある。
安全なアーキテクチャを管理するためには、コード分析ツールの分析スコープと、データの保存方法に関する設計上の選択肢を明確に区別する必要がある。TypeScriptファイル内に大量の設定データを直接保存することには、インラインの型チェックや自動補完の利点があるが、品質ゲートで処理されるコードの総行数が増大し、データ構造の繰り返しによってコード重複の数値が人為的に高まる可能性がある。
一方、大量のデータをJSONファイルのような外部ファイルに分離して保存することは、ソースコードをクリーンに保ち、生データを分析スコープから外すことができる。しかし、JSONファイルはTypeScriptのような型チェックの恩恵を直接受けられないため、データが期待される形式から逸脱した場合に、コンパイル時ではなく実行時にエラーが発生するリスクがある。これを防ぐためには、JSONデータが正しい構造を持っているかを検証する「明示的なバリデーション層」を導入する必要がある。
推奨されるアプローチは、JSONモジュールからの型付きインポートと、ビルドパイプラインでの自動回帰テスト、そして厳密なデータパリティチェックを組み合わせることである。これにより、開発者は型安全なAPIを扱うような感覚でデータを利用でき、同時にSonarQubeはアプリケーションの実行ロジックやヘルパー関数といった純粋なコードの品質チェックに集中できる。
例えば、AstroとTypeScriptを用いたデータが多い静的サイトの例では、初期実装でタイマーの期間やオーディオ設定、カテゴリタグなどのデータが共有のTypeScript定数ファイルに記述されていた。これにより、生データ配列がコード分析ツールの重複スキャン対象となり、問題として指摘された。
この解決策として、生データを以下のような構造化されたJSONファイルに移動し、TypeScriptの型システムを活用して厳密な型アサーションでインポートする方法がとられた。
1{ 2 "timerDefaultDuration": 300, 3 "allowedCategories": [ 4 "focus", 5 "short-break", 6 "long-break" 7 ], 8 "audioEnvelope": { 9 "attack": 0.05, 10 "release": 0.1 11 } 12}
アプリケーションがこの外部データを期待通りに扱えるように、ビルドパイプラインの中で、JSONデータが正しい構造を持っているかを検証するテストを実行する。TypeScriptの検証ユーティリティは、サイトがコンパイルされる前に、インポートされたJSONデータがアプリケーションが期待するデータの形式と一致していることを確認する。以下は、その検証処理の簡略化した例である。
1import registryData from './timer-registry.json'; 2 3interface TimerRegistry { 4 timerDefaultDuration: number; 5 allowedCategories: string[]; 6 audioEnvelope: { 7 attack: number; 8 release: number; 9 }; 10} 11 12function validateRegistry(data: unknown): asserts data is TimerRegistry { 13 if (typeof data !== 'object' || data === null) { 14 throw new Error('Invalid registry format'); 15 } 16 const reg = data as Record<string, unknown>; 17 if (typeof reg.timerDefaultDuration !== 'number') { 18 throw new Error('Missing or invalid timerDefaultDuration'); 19 } 20 if (!Array.isArray(reg.allowedCategories)) { 21 throw new Error('Missing or invalid allowedCategories'); 22 } 23} 24 25validateRegistry(registryData); 26export const registry: TimerRegistry = registryData;
この validateRegistry 関数は、timer-registry.json から読み込まれたデータが TimerRegistry という型定義に沿っているかを厳しくチェックする。これにより、不正なデータがアプリケーションに渡されるのを防ぎ、ランタイムエラーのリスクを低減できる。
このような対策を講じた結果、品質ゲートの実行結果とテストスイートの網羅率が改善された。静的な設定データをJSONに移動し、重複していたヘルパー関数を整理した後、メインブランチでの認証済みスキャンでは、コード分析がクリーンな状態であることが確認された。分析対象となったコードのメトリクスでは、9000行以上のソースコードにおいて、重複するブロックも重複する行もゼロと記録され、全体的な品質ゲートも「合格」となった。また、すべてのテストが成功し、データの外部化がアプリケーションの実行時の動作に悪影響を与えないことが証明された。 開発者は、ローカル環境でのコードチェックツールやプルリクエストの承認ゲートは「新規コード」のチェックが主な役割であり、全体的な品質に関する決定的なメトリクスを得るためには、メインブランチ上での認証済みの分析実行が必要であることを理解すべきである。コードの重複に関するメトリクスとデータ構造の間の明確な区別を維持することで、品質ゲートがアプリケーションのロジックの健全性を正確に反映するようになる。