Typecheck in CI
Running tsc --noEmit in CI catches type errors that ESLint alone misses and blocks merges before broken builds reach production.
Search across all documentation pages
Running tsc --noEmit in CI catches type errors that ESLint alone misses and blocks merges before broken builds reach production.
Quick-reference recipe card - copy-paste ready.
{
"scripts": {
"typecheck": "tsc --noEmit -p tsconfig.json"
}
}- run: npm ci
- run: npm run typecheckWhen to reach for this:
noEmit) for faster feedback than full build.// tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"noEmit": true,
"skipLibCheck": true,
"types": ["node"]
},
"include": ["src", "test"]
}// package.json
{
"scripts": {
"typecheck": "tsc --noEmit",
"build": "tsc -p tsconfig.build.json"
}
}# .github/workflows/ci.yml
name: ci
on: [pull_request]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "24.18.0"
cache: npm
- run: npm ci
- run: npm run typecheck
- run: npm run lint
- run: npm test
- run: npm run buildWhat this demonstrates:
typecheck runs before tests to fail in seconds on signature errors.strict: true is non-negotiable for backend services.build still runs in CI to verify emit config and dist/ output.tsc --noEmit typechecks without writing files - ideal CI merge gate.tsconfig.build.json emits only src/ for production bundles.skipLibCheck: true speeds CI by skipping .d.ts validation in node_modules.turbo run typecheck with dependsOn: ["^build"] when types come from compiled deps.| Step | Why this order |
|---|---|
npm ci | Deterministic graph |
typecheck | Fast signal on types |
lint | Logic and style |
test | Behavior |
build | Emit verification |
{
"compilerOptions": {
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true
}
}@types/node with Node 24.noEmit path hides test-only type errors if build excludes test/. Fix: dedicated typecheck including tests..d.ts from packages. Fix: turbo run typecheck --filter=api... after ^build.npm run typecheck as source of truth.skipLibCheck hiding bad overrides - Rare but painful. Fix: periodic local run with skipLibCheck: false on upgrades.@ts-expect-error without comments accumulates. Fix: lint ban excessive expect-error directives.| Alternative | Use When | Don't Use When |
|---|---|---|
tsgo / native preview | Experimenting on speed | Production CI needs stable TSC |
| Type-aware ESLint only | Supplement, not replace | You skip tsc entirely |
build as only gate | Tiny single-file scripts | Tests excluded from build config |
Yes, unless tests use a separate tsconfig.test.json referenced by a second CI step.
Use project references and Turbo caching. Typecheck packages in dependency order once, cache hashes.
Nest uses tsconfig.build.json with emitDecoratorMetadata. Typecheck should use same experimentalDecorators settings as build.
Yes in separate CI jobs after npm ci. Both must pass before merge.
allowJs + checkJs for gradual migration. Prefer full TS for new services.
--incremental with cached .tsbuildinfo helps large repos; Turbo often suffices.
tsconfig paths must match runtime (or use built package names). Misaligned aliases pass locally with tsx but fail tsc.
Yes when build include is narrower than typecheck. Tests and scripts stay covered.
Enable one new flag per sprint with a tracking issue; CI enforces once zero errors.
Express 5 ships types; remove duplicate @types/express if both are installed.
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.
Reviewed by Chris St. John·Last updated Jul 18, 2026