DEV Community

Cover image for Sonar Duplication in TypeScript Static Sites
Raylabs
Raylabs

Posted on Originally published at raylabs.app

Sonar Duplication in TypeScript Static Sites

When a static site grows to include extensive configuration registries, locale catalogs, and curated content records, code analysis tools often flag repeated structures or raw data. Developers frequently wonder how SonarQube handles overall duplication when large amounts of data live in TypeScript files versus dedicated data structures. The core question is whether moving repeated records out of source code and into JSON changes the analyzer metrics or merely hides repetitive data from the report. SonarQube evaluates code duplication strictly within the files included in its analysis scope. When a project stores large registries in TypeScript, those files are scanned, and any structural repetition is measured. If those same records are converted to JSON and excluded by the Sonar configuration, the duplication metrics drop to zero for those payloads simply because they are no longer part of the analyzed source code. This leaves developers with a methodological distinction to keep in mind. A zero percent duplication score on the main branch means that the analyzed TypeScript and JavaScript code contains no measured duplication, but it does not mean the entire repository is free of repeated text or data values. Understanding this boundary prevents confusion when comparing local file contents against authenticated CI scan results. For a related implementation, see Static Analysis Lint Detekt Ci.

To manage this architecture safely, developers must separate analyzer scope from data storage trade-offs. Storing large registries in TypeScript offers inline type checking and immediate autocompletion, but it increases the lines of code processed by quality gates and can artificially inflate duplication numbers if similar structures repeat across components. Moving registries into JSON files keeps the source code clean and removes raw data from the analyzer scope, but it requires an explicit validation layer to maintain type safety. Without strict schema checks, runtime errors can slip past the compiler when data files drift from their expected shapes. The correct approach relies on typed imports from JSON modules, backed by automated regression tests and exact data parity checks during the build pipeline. This design preserves the developer experience of type-safe APIs while keeping the SonarQube scanner focused purely on executable logic and helper functions. For a related implementation, see Reclaim Macos Developer Storage Safely.

Consider an architectural example from a data-heavy static site using Astro and TypeScript. In the initial implementation, timer durations, audio envelope configurations, and category tags lived inside a shared TypeScript constants file. While convenient, this approach exposed raw data arrays directly to the linter and the code duplication scanner. Refactoring this setup involves moving the raw payloads into structured JSON files and importing them with strict type assertions. The following configuration illustrates how TypeScript consumes the externalized registry while retaining strict validation.

{
  "timerDefaultDuration": 300,
  "allowedCategories": [
    "focus",
    "short-break",
    "long-break"
  ],
  "audioEnvelope": {
    "attack": 0.05,
    "release": 0.1
  }
}
Enter fullscreen mode Exit fullscreen mode

To ensure this external data satisfies the application requirements without relying on SonarQube to catch data repetition, the build pipeline runs a series of parity and regression checks. The following TypeScript snippet demonstrates how a validation utility confirms the imported JSON matches the expected runtime interface before the site compiles.

import registryData from './timer-registry.json';

interface TimerRegistry {
  timerDefaultDuration: number;
  allowedCategories: string[];
  audioEnvelope: {
    attack: number;
    release: number;
  };
}

function validateRegistry(data: unknown): asserts data is TimerRegistry {
  if (typeof data !== 'object' || data === null) {
    throw new Error('Invalid registry format');
  }
  const reg = data as Record<string, unknown>;
  if (typeof reg.timerDefaultDuration !== 'number') {
    throw new Error('Missing or invalid timerDefaultDuration');
  }
  if (!Array.isArray(reg.allowedCategories)) {
    throw new Error('Missing or invalid allowedCategories');
  }
}

validateRegistry(registryData);
export const registry: TimerRegistry = registryData;
Enter fullscreen mode Exit fullscreen mode

Evaluating the success of this remediation requires examining both quality gate outcomes and test suite coverage. After moving static registries to JSON and consolidating repeated timer helpers, authenticated scans on the main branch confirm a clean analysis state. The analyzed code metrics record zero duplicated blocks and zero duplicated lines across more than nine thousand lines of source code. Furthermore, New Code duplication remains safely below the configured threshold, and the overall Quality Gate status resolves as passed. Local test suites execute successfully across all unit, integration, and end-to-end browser tests, proving that externalizing data arrays does not disrupt runtime behavior. Developers examining these reports must remember that local clone tools and pull request gates primarily act as proxies or New Code checks, whereas definitive overall metrics require an authenticated analysis run on the main branch. Maintaining clear distinctions between code duplication metrics and data registry structures ensures that quality gates accurately reflect the health of the application logic.

Top comments (1)

Collapse
 
suppdevbot profile image
DEV SUPPORTS •

You need to verify your account.

Enter fullscreen mode Exit fullscreen mode

tr.ee/dev-to