Run the compiler as a required CI check that exits non-zero on any error, and set noEmitOnError so a failed build writes no output. The TypeScript reference says noEmitOnError defaults to false, so by default the compiler can produce JavaScript even when it reported errors.
What the reference says
The tsconfig reference describes noEmitOnError as: do not emit compiler output files such as JavaScript, source maps or declarations if any errors were reported. It notes the default is false, which suits watch style work where you want to see results before every error is fixed. That default is convenient on a laptop and unsafe as a release default.
The Node.js TypeScript page uses noEmit in its recommended config for projects that only execute .ts files. That variant writes nothing and acts as a pure check.
Two settings, two jobs
| Setting | Effect | Best for |
|---|---|---|
| noEmitOnError | Emits nothing if any error is reported | Builds that produce JavaScript |
| noEmit | Never emits, only checks | CI type check, or projects Node runs directly |
Why this matters for security
Strict flags only help if they gate something. If a build prints type errors and still produces a bundle, and the pipeline only looks at the bundle, the errors are advice. A missing null check that the compiler found can then ship. Make the rule binary: errors mean no artifact.
Steps
- Turn on
strictand the extra flags you want, see tsconfig flags beyond strict. - Add
noEmitOnError: trueto the build tsconfig. - Add a CI job that runs the compiler with
--noEmiton every pull request. Name it clearly, such as "typecheck". - Mark that job as a required status check on the protected branch.
- Make the release job depend on a passing typecheck job, so a manual rerun of the build step cannot skip it.
- Do not hide errors with
@ts-ignorecomments without a reason. Prefer@ts-expect-errorwith a note, which fails when the error disappears, so stale suppressions get noticed. - Count suppression comments in CI and fail if the number grows.
Compiler versus bundler
Many bundlers and fast transpilers strip types without checking them. They are fast because they skip the type check. If your build uses one, the compiler check must run separately, otherwise nothing enforces your types. This is the same gap described in running TypeScript directly in Node.
What a passing check does not mean
A clean type check says the types agree. It does not say input is validated or that dependencies are safe. Keep schema validation at boundaries and scan your lockfile.
Try it yourself
Open the pull request gate demo and watch a failing check block a change. Your typecheck job should behave the same way.
Click through it at your own pace, or open it full screen.
Safeguard policies and gates can add dependency checks to the same pull request flow. A free scan shows your lockfile's findings.
Quick check
Is noEmitOnError on by default?
No. The TypeScript reference says it defaults to false.
Do I still need a typecheck job if my bundler builds fine?
Yes. Bundlers that strip types do not check them, so run the compiler as its own required check.
Sources
Last reviewed: 19 October 2026.