Skip to main content

Best Practices

Practical TSConfig guidance for Nova that uses small, composable preset chains. Favor explicit wiring and predictable compiler behavior.

Dos and Don'ts

  • What: Use a layered "extends" chain in this order: essentials → strict → environment/framework → platform → tool → your custom overrides.
  • Why: The base loads first; the child overrides it. A clear order keeps policy layered and reviewable.
  • What: Keep them in sync by using "module": "nodenext" with "moduleResolution": "nodenext" for Node ESM, or "module": "esnext" with "moduleResolution": "bundler" for bundlers.
  • Why: Mismatched values make TypeScript and your runtime resolve imports differently, which causes "cannot find module" errors. Pairing keeps resolution consistent.
  • What: Define import aliases in paths without setting the deprecated TypeScript compiler baseUrl option.
  • Why: TypeScript 6 resolves paths entries relative to the TSConfig file when baseUrl is absent.
  • What: Path-related settings like paths, rootDir, outDir, include, and exclude belong in the app's tsconfig.json file, not in Nova presets.
  • Why: TypeScript evaluates these against the file's location. Local wiring avoids resolution mistakes across different repos.
  • What: Set rootDir to the source directory for projects that use tsc or nova utility transpile to emit output.
  • Why: TypeScript 6 no longer lets emitted output layout depend on an inferred source root when that root differs from the TSConfig directory.
  • What: Use types to list the type packages you installed (e.g., @types/node, @types/react), or write [] when the config needs no ambient packages.
  • Why: Makes type sources explicit and under the app's control. Reduces surprises from transitive @types dependencies.
  • What: If your project does not use tsc to build, set "isolatedModules": true and "noEmit": true in the app's tsconfig.json.
  • Why: Bundlers and frameworks compile each file independently. isolatedModules enforces per-file safety. noEmit prevents TypeScript from writing JavaScript when another tool already emits output.
  • What: Do not expect array settings like lib to spread values from the base.
  • Why: In tsconfig, these arrays replace the parent. This is a config limitation, not JS behavior.
  • What: Avoid settings that create unpredictability like incremental, composite, and assumeChangesOnlyAffectDirectDependencies in app tsconfig.json files.
  • Why: These introduce cache drift across environments. Prefer deterministic builds.
  • What: Do not set typeRoots without a strong reason.
  • Why: It limits which @types packages are visible. Defaults usually work best.

Troubleshooting

Find issues by using the command below to print the effective config:

bash
tsc --showConfig --project ./path/to/tsconfig.json