skip to content

In a Vite-based Vue 3 TypeScript project, why can the dev server and production build succeed despite type errors, and what catches them?

level: juniorimportance: must knowfreq 60%

answer

  1. transpile-only pipeline
  2. types stripped per file
  3. a separate checker
  4. editor plus CI script

basics

~20 s

Vue's Vite-based setup is transpilation-only: the dev server and bundler strip types from each file without type-checking, to stay fast. Type errors are caught by the editor's Vue language tools and by running vue-tsc --noEmit in a script or CI.

solid answer

~40 s

The Vue TypeScript guide states that with a Vite-based setup the dev server and bundler are transpilation-only and do not type-check. Each file's TypeScript is stripped in isolation, which keeps the dev server fast, but it means a wrong prop type or a typo in a template expression still builds and ships. Checking is a separate job: during development the official editor extension (Vue - Official) shows errors as you type, and on the command line `vue-tsc --noEmit`, a wrapper around `tsc` that understands `.vue` files, checks the whole project. Teams put `vue-tsc` in a `type-check` script and run it in CI, or before the build, so type errors fail the pipeline; it can also run in watch mode next to the dev server.

code

vue · 11 lines
vue
<script setup lang="ts">
import { ref } from 'vue'

const count = ref<number>(0)
</script>

<template>
  <!-- builds fine with a transpile-only pipeline; vue-tsc reports:
       Property 'toUppercase' does not exist on type 'number' -->
  <p>{{ count.toUppercase() }}</p>
</template>

go deeper

for a junior

Remember that the Vue Vite pipeline only strips types; type errors are reported by the editor extension and by vue-tsc, not by the build.

for a middle

Explain why transpile-only is per file and fast, and why the checker must see the whole program, including SFC templates, which only vue-tsc can do.

for a senior

Make type-checking a required CI step with vue-tsc, choose --noEmit or --build to match the tsconfig layout, and keep it off the dev server's critical path.

for a principal

Set the team's quality gate: which checks block a merge, where they run, and how fast they must be so developers do not route around them.

## What the build actually does with TypeScript In a Vue 3 project built with Vite, TypeScript in `.ts` files and in `<script lang="ts">` blocks is **transpiled**, not **type-checked**. The Vue TypeScript guide says so directly: with a Vite-based setup, the dev server and the bundler are transpilation-only and do not perform any type-checking. Transpiling means removing type annotations and turning the rest into JavaScript, **one file at a time**. A type checker instead needs the whole program: every import, every declaration, every component's props. The guide adds that the transpiler works under single-file limitations, which is why the scaffolded `tsconfig` enables `isolatedModules` (or the stricter `verbatimModuleSyntax`) so that code which a per-file transpiler cannot handle is flagged by the checker. The consequence: the following component builds and runs, and fails only at runtime or never visibly. ```vue <script setup lang="ts"> const props = defineProps<{ price: number }>() </script> <template> <span>{{ props.price.toFixd(2) }}</span> </template> ``` ## Why Vue chose this split - **Speed**: type-checking a whole project on every file change would slow the dev server down; stripping types per file is cheap. - **One source of truth**: errors should map back to the source you wrote. The guide criticises checking inside the webpack transform pipeline with `ts-loader`: it can only check post-transform code, which does not align with the errors shown by the IDE or `vue-tsc`. - **Separation**: the editor already runs a type checker in a separate process, so doing it again inside the bundler is a poor trade. ## What catches the errors | Where | Tool | When it runs | |---|---|---| | Editor | Vue - Official extension (Vue language server) | continuously while you type | | Terminal, dev time | `vue-tsc --noEmit --watch` | alongside the dev server | | Script or CI | `vue-tsc --noEmit` (or `vue-tsc --build` with project references) | before or alongside the build | `vue-tsc` is a wrapper around `tsc` that also understands `.vue` files, including the expressions in templates, so it reports errors plain `tsc` cannot see. ## Wiring it into the pipeline 1. Add a script such as `"type-check": "vue-tsc --noEmit"`. 2. Run it in CI as its own step, so a type error fails the pipeline even though `vite build` succeeds. 3. Optionally make the production build depend on it, for example by running both in the build script. 4. Keep it fast by checking the right tsconfig: with project references, point it at the app config or use `--build`. ```json { "scripts": { "type-check": "vue-tsc --noEmit", "build": "vue-tsc --noEmit && vite build" } } ``` ## What each layer can and cannot catch | Layer | Catches | Misses | |---|---|---| | Transpile step (dev server, build) | syntax errors, invalid SFC structure, template compile errors | every type error | | Editor extension | type errors in open files, including templates | files nobody opens; teammates without the extension | | `vue-tsc` in CI | type errors across every file the tsconfig includes | files outside that tsconfig's `include` | The middle row explains why the editor alone is not a gate: it reports what a developer looks at, not the whole project. The last row explains why the CI command must point at the right configuration. ## Common misconceptions - **"`lang=\"ts\"` makes the build strict."** It only tells the transpiler and the language tools that the block is TypeScript. - **"The dev server's error overlay shows type errors."** It shows syntax and compile errors from transpiling, not type errors. - **"`tsc --noEmit` is enough."** Plain `tsc` does not understand `.vue` files, so it cannot check SFCs or their templates. - **"Editor errors will stop a bad commit."** Only if something in CI or a hook runs `vue-tsc`; the editor cannot block a merge.

  • Why does the Vue TypeScript guide advise against type-checking inside the webpack build with ts-loader?
    Checking needs the whole module graph, while a loader sees one transformed module at a time. The guide lists three problems: `ts-loader` only checks post-transform code, so errors do not line up with the IDE or `vue-tsc`; checking in the same process slows the whole build; and the IDE already type-checks in a separate process.
  • How can a Vue developer see type errors from the terminal during development without slowing the dev server?
    Run `vue-tsc --noEmit --watch` in a second terminal, or in parallel with the dev server from one script. It re-checks on file changes in its own process, so the dev server stays transpile-only and fast, and the editor extension still gives instant feedback per file.

The bundler is a printing press: it prints whatever manuscript it is given, quickly, without proofreading. vue-tsc is the proofreader, and a book only gets proofread if someone puts that step in the workflow.

saying these in an interview costs you the question

  • Vite type-checks .vue files during vite build, so a green build means no type errors.
  • Adding lang="ts" to the script block makes the dev server reject type errors.
  • Plain tsc --noEmit also checks templates in single-file components.
  • The dev server's error overlay reports TypeScript type errors.
  • Editor squiggles are enough to keep type errors out of main.