Type checking needs the whole program: it resolves imports across files, builds types, and reports errors. Transpiling (emit) turns TypeScript into JavaScript. Because types are erased, most of that is deleting syntax, and it can be done one file at a time without knowing any type.
tsc does both, and they are independent: by default it writes output even when it reports errors (noEmitOnError is false). esbuild, swc and Babel's TypeScript preset only transpile. Babel's docs say plainly that its plugin "does not add the ability to type-check", and Vite's docs say the same of its transformer. They are faster because they skip the expensive part, and they can work per file in parallel.
So in a typical setup the two jobs are split:
- the bundler or runtime transpiles (fast feedback, no type information);
tsc --noEmittype-checks, in CI and in the editor. The TSConfig reference describesnoEmitas leaving emit to "another tool like Babel, or swc" while you keep TypeScript as the type-checker.
The catch is that a few constructs cannot be compiled correctly without type information. The classic one is an import or re-export that might be a type: a one-file tool cannot tell whether Config is erased or kept. isolatedModules makes tsc report those constructs (for example TS1205 for export { Config } from "./types"), so the code stays safe for per-file tools. esbuild's docs tell you to enable it.
--noCheck (TSConfig reference: released in 5.6) is the inverse of --noEmit: emit and skip type checking, useful when a faster tool already checked, such as in a separate CI job.
Gap to remember: if only the bundler runs on deploy, type errors ship.