@aleph-alpha/tsconfig-frontend

Shared strict TypeScript presets (base, vue-app, lib, node) for Aleph Alpha frontend projects
83,882
Install
npm install @aleph-alpha/tsconfig-frontend
Latest Version:0.7.3
License:Apache-2.0
Last Updated:Aug 11, 2026
Links: Repository  ·  npm
Maintainer: oleksandr.honskyi

@aleph-alpha/tsconfig-frontend

Shared, strict-by-default TypeScript configuration for Aleph Alpha frontend projects.

It ships a small set of composable presets. The idea: everything that is genuinely shareable (language target, module resolution, the strict family, common quality flags) lives here in one reviewed place; everything project-specific (paths, include/exclude, emit layout, environment types) stays in the consuming project. Every compilerOption a preset sets can be overridden locally — presets are a baseline, not a straitjacket.

pnpm add -D @aleph-alpha/tsconfig-frontend

Why four presets instead of one?

base is the single extensible core. The other three exist because TypeScript compilation has distinct, mutually-exclusive contexts that one config cannot serve at once — and which every frontend project already separates (a tsconfig.app.json for browser code + a tsconfig.node.json for tooling is the de-facto standard):

  • Browser/Vue code needs DOM libs + JSX; Node tooling must not have them.
  • Node tooling needs types: ["node"]; browser code must not pollute its global scope with them.
  • Packages that emit .d.ts need declaration/composite; apps that don't emit would error on them.

Collapsing to one config wouldn't remove that configuration — it would just copy-paste it into every project (and invite mistakes like a too-low lib). The presets move it into one place.

Presets at a glance

Preset Extends Use it for Emits?
/base — Framework-agnostic TS: shared libs, utils, pure logic no
/vue-app — (mirrors @vue/tsconfig) Vue SFCs & browser code (apps and Vue component libs) no (Vite/dts plugin emits)
/lib ./base Buildable libraries that emit .d.ts via tsc yes
/node — (mirrors @tsconfig/node24) Node tooling: vite.config, vitest.config, eslint.config, scripts no

The bare @aleph-alpha/tsconfig-frontend entry still resolves (it maps to the legacy flat config) for backwards compatibility. New code should extend one of the four presets above.


/base

What — the framework-agnostic strict baseline. No DOM, no Node, no JSX assumptions.

Sets (the shareable core all other presets build on conceptually):

{
  "target": "es2022",
  "module": "esnext",
  "moduleResolution": "bundler",
  "lib": ["es2022"],

  // strict family
  "strict": true,                     // implies noImplicitAny, strictNullChecks, ...
  "noUncheckedIndexedAccess": true,   // arr[i] / obj[key] is T | undefined
  "verbatimModuleSyntax": true,       // type-only imports must use `import type`
  "useDefineForClassFields": true,

  // safe bug-catchers (no emit impact, catch real bugs)
  "noImplicitOverride": true,         // overrides must be marked `override`
  "noFallthroughCasesInSwitch": true,
  "noImplicitReturns": true,

  // ergonomics
  "isolatedModules": true,
  "esModuleInterop": true,
  "forceConsistentCasingInFileNames": true,
  "importHelpers": true,
  "resolveJsonModule": true,
  "skipLibCheck": true,
  "sourceMap": true
}

Why — this is the strictness contract for the whole org. noUncheckedIndexedAccess and the bug-catchers catch real defects; verbatimModuleSyntax keeps imports unambiguous for bundlers.

When to use — pure TypeScript with no browser or Node assumptions (e.g. a utils package).

How to use

{
  "extends": "@aleph-alpha/tsconfig-frontend/base",
  "compilerOptions": {
    "baseUrl": ".",
    "paths": { "@/*": ["src/*"] }
  },
  "include": ["src/**/*.ts"]
}

/vue-app

What — config for Vue Single-File Components and browser code. Mirrors the Vue team's official @vue/tsconfig/tsconfig.dom.json, inlined verbatim rather than extended (see Why the presets are self-contained).

Sets (on top of the inlined @vue/tsconfig baseline, which already provides strict, verbatimModuleSyntax, useDefineForClassFields, jsx: "preserve", jsxImportSource: "vue", noEmit):

{
  "lib": ["ES2022", "DOM", "DOM.Iterable"],
  "types": ["vite/client"],
  "noImplicitOverride": true,
  "noFallthroughCasesInSwitch": true,
  "noImplicitReturns": true
}

Why — .vue files need JSX + DOM libs + Vue's jsxImportSource. Mirroring @vue/tsconfig (rather than re-deriving it) gives us the Vue team's battle-tested defaults; re-merge the upstream file by hand when bumping it.

When to use — any app or component library containing .vue files. The actual JS/.d.ts output is produced by Vite / vite-plugin-dts, not tsc — so noEmit: true (from the inlined baseline) is correct here.

How to use

{
  "extends": "@aleph-alpha/tsconfig-frontend/vue-app",
  "compilerOptions": {
    "baseUrl": ".",
    "paths": { "@/*": ["src/*"] }
  },
  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}

/lib

What — for buildable library packages that emit type declarations through tsc.

Sets (extends ./base):

{
  "declaration": true,
  "composite": true,
  "lib": ["es2022", "dom", "dom.iterable"]
}

Why — emitting libraries need declaration (to ship .d.ts) and composite (project references / incremental builds). These would be wrong for apps, so they live in their own preset.

When — a non-Vue package whose .d.ts is produced by tsc (not by Vite). Typically paired with a local outDir/rootDir.

How

{
  "extends": "@aleph-alpha/tsconfig-frontend/lib",
  "compilerOptions": {
    "outDir": "dist",
    "rootDir": "src",
    "paths": { "@/*": ["src/*"] }
  },
  "include": ["src/**/*.ts"]
}

Note: composite is incompatible with outFile. If a package bundles its declarations into a single file via outFile, extend /base instead and add declaration + emitDeclarationOnly locally (see the outFile gotcha below).


/node

What — config for Node-executed tooling files, not shipped browser code.

Sets (on top of the inlined @tsconfig/node24 baseline):

{
  "noEmit": true,
  "module": "esnext",
  "moduleResolution": "bundler",
  "types": ["node"],
  "noImplicitOverride": true,
  "noFallthroughCasesInSwitch": true,
  "noImplicitReturns": true
}

Why — vite.config.ts, vitest.config.ts, eslint.config.ts, build scripts, etc. run in Node and need Node globals/types, not the DOM. Keeping them in a separate config means browser code never accidentally pulls in Node types and vice-versa.

When to use — the tsconfig.node.json that type-checks your config/tooling files.

How to use

{
  "extends": "@aleph-alpha/tsconfig-frontend/node",
  "include": ["vite.config.ts", "vitest.config.ts", "eslint.config.ts"]
}

What the package owns vs. what the project owns

In the presets (shareable): target, module/moduleResolution, baseline lib, the strict family, the bug-catchers, and the emit shape for /lib. Change these only by changing the package (so every consumer moves together).

In the project (always local & overridable): paths + baseUrl, include/exclude, rootDir/outDir/tsBuildInfoFile, environment types, references, experimentalDecorators/emitDecoratorMetadata, and any per-project strict opt-out/opt-in.

Why the presets are self-contained

No preset here extends a config from another package — /vue-app and /node inline their upstream baselines (@vue/tsconfig, @tsconfig/node24) instead of extending them.

TypeScript resolves a package-style extends the way Node resolves an import, walking up node_modules and following symlinks. Other tools that read tsconfig.json do not. Playwright, for one, looks only in <the extending file>/node_modules/… — so with pnpm, where a package's own dependencies live in the store rather than inside the package folder, it cannot follow node.json → @tsconfig/node24 and fails to load the config entirely:

Failed to load tsconfig file at …/apps/<app>-e2e/tsconfig.json:
Failed to resolve "extends" path "@tsconfig/node24/tsconfig.json"
referenced from …/node_modules/@aleph-alpha/tsconfig-frontend/node.json

Keeping every preset self-contained removes that whole class of failure — for Playwright and for any other tool with a naive resolver. The cost is that upstream bumps must be re-merged by hand; the provenance (package + exact version) is recorded in a "//" key at the top of each affected preset.

Relative extends within this package (/lib → ./base) is fine — every tool handles that.

@vue/tsconfig and @tsconfig/node24 remain in this package's dependencies even though no preset extends them anymore. That is deliberate: dropping them would break any consumer that extends either package directly and relies on it arriving transitively. They are kept for backwards compatibility and slated for removal in a future major — if you extend either one directly, declare it in your own package.json.

Common overrides & gotchas

These come up in real migrations — all are legitimate local overrides:

  • Need a newer JS built-in (Array.prototype.toSorted = ES2023, etc.)? @vue/tsconfig and our presets target a conservative lib. Bump it locally: "lib": ["ES2023", "DOM", "DOM.Iterable"]. This only affects type-checking — Vite controls the real browser target.
  • Code uses window/DOM but extends /base? Add DOM to lib locally: "lib": ["ES2022", "DOM"]. (Or use /vue-app, which includes DOM.)
  • outFile for a single bundled .d.ts? outFile is incompatible with both composite and verbatimModuleSyntax. Extend /base, set declaration + emitDeclarationOnly + outFile, and override "verbatimModuleSyntax": false locally.
  • Node tooling that isn't a config file (e.g. a Node-targeted library)? Extend /base and add "types": ["node"] rather than /node (which forces noEmit).
  • Migrating a codebase that predates noImplicitAny? If turning it on surfaces a large backlog, you may temporarily set "noImplicitAny": false locally and burn the errors down later — but keep it on in base so new/clean code stays strict.

Convention: document the reason for any strict opt-out in the PR description, not as a comment in the tsconfig — so it doesn't rot when the override is later removed.

Related Packages

@ouuan/tsconfig-vue

ouuan's personal TypeScript config for Vue

4,217 0
@vue/tsconfig

A base TSConfig for working with Vue.js

71,368,083 360
pckg-app-frontend

Frontend pckg package

5,788 0
@tsconfig/nuxt

A base TSConfig for working with Nuxt.

5,295 7,886
@nlo/nuxt-common

Nuxt3 layer for the common user interface of NLO lottery websites

12,245

Version History

Version Released License
0.7.3 Apache-2.0
0.7.2 Apache-2.0
0.7.1 Apache-2.0
0.7.0 Apache-2.0
0.6.0 Apache-2.0
Pre-releases (1)
Version Released License
0.3.2-canary.6a2fa49c.0 Apache-2.0