In a create-vue TypeScript project, why does tsconfig.json list no files but reference tsconfig.app, tsconfig.node and tsconfig.vitest, and what does npm run build run?
answer
- one root, several projects
- browser code, tool configs, tests
- different globals per environment
- type-check and build in parallel
- the alias lives in two places
basics
~20 sThe root tsconfig.json only references separate projects: app code for the browser, tool configs for Node, and tests for jsdom, each with its own globals. npm run build runs vue-tsc --build and vite build in parallel.
solid answer
~30 screate-vue writes a **solution-style** `tsconfig.json`: `files: []` plus `references` to `tsconfig.node.json`, `tsconfig.app.json` and, with Vitest, `tsconfig.vitest.json`. Each covers one environment: `tsconfig.app.json` extends `@vue/tsconfig/tsconfig.dom.json` for `src` and `.vue` files and excludes tests; `tsconfig.node.json` extends `@tsconfig/node24` for `vite.config.*`, `vitest.config.*` and other tool configs with `noEmit`; `tsconfig.vitest.json` extends the app config for `src/**/__tests__/*` with Node and jsdom types. `npm run build` is `run-p type-check "build-only {@}" --`: `vue-tsc --build` checks every referenced project while `vite build` bundles. The `@/*` alias is declared both in `vite.config` and in `tsconfig.app.json` paths.
code
json · 8 lines{
"files": [],
"references": [
{ "path": "./tsconfig.node.json" },
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.vitest.json" }
]
}go deeper
Recall that npm run build type-checks and bundles, and that the @ alias points at src.
Explain the three projects the root references, which files each includes and why tests and tool configs are separate.
Show the parallel build script, how arguments reach vite build, and the alias drift and missing-include mistakes you would catch in review.
Decide how far teams may diverge from the generated TypeScript layout, and which settings, such as noUncheckedIndexedAccess, become house policy.
## Why one tsconfig is not enough A Vue project contains code that runs in **different environments**: - application code in the browser, with DOM types and `.vue` files; - tool configuration such as `vite.config.ts`, which runs in Node; - unit tests, which run in Node with a simulated DOM from `jsdom`. One `tsconfig.json` would have to include DOM and Node types everywhere, so browser code could call Node APIs without an error. create-vue instead writes a **solution-style** root: `files: []` and a list of `references`, one per environment. Each referenced file is a separate TypeScript project. ## The generated files | File | Extends | Includes | Notable settings | |---|---|---|---| | `tsconfig.json` | nothing | no files | `references` to the others | | `tsconfig.app.json` | `@vue/tsconfig/tsconfig.dom.json` | `env.d.ts`, `src/**/*`, `src/**/*.vue`; excludes `src/**/__tests__/*` | `paths` for `@/*`, `noUncheckedIndexedAccess: true` | | `tsconfig.node.json` | `@tsconfig/node24` | `vite.config.*`, `vitest.config.*`, `cypress.config.*`, `playwright.config.*`, `eslint.config.*` | `module: preserve`, `moduleResolution: bundler`, `types: ['node']`, `noEmit` | | `tsconfig.vitest.json` | `./tsconfig.app.json` | `src/**/__tests__/*`, `env.d.ts` | `lib: []`, `types: ['node', 'jsdom']` | Each project writes its `.tsbuildinfo` under `node_modules/.tmp`, so incremental checks stay out of the repository root. `env.d.ts` references `vite/client`, which types `import.meta.env` and asset imports. ## What npm run build runs The TypeScript layer rewrites the build scripts: ```json { "build": "run-p type-check \"build-only {@}\" --", "build-only": "vite build", "type-check": "vue-tsc --build" } ``` 1. `run-p` from `npm-run-all2` starts both scripts **in parallel**. 2. `type-check` runs `vue-tsc --build`, which follows the root's references and checks every project, including `.vue` templates. 3. `build-only` runs `vite build`, which transpiles without type-checking. 4. `{@}` forwards any arguments given after `--` to `build-only`, so `npm run build -- --mode staging` reaches Vite. If either fails, the build fails. `npm run dev` runs only Vite, so type errors surface in the editor and at build time, not in the dev server. ## The alias in two places The `@` import alias is declared twice: `vite.config` maps `@` to `./src` for bundling, and `tsconfig.app.json` maps `@/*` to `./src/*` for type-checking. Changing one without the other gives an app that builds but does not type-check, or the reverse. ## Where the types and tools come from - **`@vue/tsconfig`** supplies the browser-side base with Vue-friendly compiler options; **`@tsconfig/node24`** supplies the Node base for tool configs. - **`typescript`** is pinned to `~6.0.0` and **`vue-tsc`** to `^3.3`, so type-check behaviour only changes when you bump them. - **`env.d.ts`** holds a single reference to `vite/client`, and it is included by the app and test projects. - **End-to-end folders** get their own configs: Playwright's `e2e/tsconfig.json` and Cypress's `cypress/tsconfig.json`. The root does not reference them, so `vue-tsc --build` leaves them to the test runner; only Cypress Component Testing adds a referenced `tsconfig.cypress-ct.json`. ## Mistakes worth catching - Moving a shared helper outside `src` without adding it to an `include`: the app project no longer checks it, and imports of it are only checked as far as module resolution reaches. - Adding `"include": ["src"]` to the root `tsconfig.json`: the root is meant to hold only references, and the app project already covers `src`. - Putting a new tool config, such as a script in the project root, outside every `include`: it is then checked by no project. - Deleting `tsconfig.vitest.json` and wondering why test files lose `jsdom` types. - Running `vite build` directly in CI and shipping type errors, because the type check only lives in the `build` script.
- Why does tsconfig.vitest.json exclude nothing while tsconfig.app.json excludes the tests?Each file belongs to exactly one project. The app project excludes `__tests__` so test-only globals never leak into application code; the test project includes only the tests and reaches the application code they import through module resolution, with Node and jsdom types added.
- What does noUncheckedIndexedAccess add in the generated app config?Indexing an array or a record returns `T | undefined` instead of `T`, so code must handle a missing element. The generated comment calls it extra safety that may produce false positives; teams keep it or turn it off deliberately.
saying these in an interview costs you the question
- The empty files array in tsconfig.json means nothing is type-checked.
- npm run build only runs vite build.
- vite.config.ts is type-checked by tsconfig.app.json.
- The @ alias only needs to be declared in tsconfig paths.
- Test files share the app project's types and need no separate config.