TypeScript CI Quality Gates: The Complete Checklist (2026)
By Nark Team
A complete TypeScript CI quality gate runs type checking, linting, formatting, tests, security scanning, and dependency error handling on every pull request. Most TypeScript projects cover the first four. The last two — security scanning and dependency error handling — are where production crashes and vulnerabilities slip through.
Quick Answer: The six CI quality gates for TypeScript are: (1)
tsc --noEmit, (2) ESLint, (3) Prettier, (4) Vitest/Jest, (5) security scanning (Semgrep or npm audit), and (6) Nark for dependency error handling. Here's a ready-to-copy GitHub Actions workflow that runs all six.
The Six TypeScript CI Quality Gates
Gate 1: TypeScript Type Checking
npx tsc --noEmit
Catches type mismatches, missing properties, null dereferences (with strictNullChecks), and structural errors. Every TypeScript project should run this in CI — not just at deploy time.
Common mistake: Running only next build or tsc for production builds, not in CI on PRs. Type errors that compile in dev but fail in prod catch you at the worst time.
Gate 2: ESLint
npx eslint src --ext .ts,.tsx --max-warnings 0
Catches language-level anti-patterns, unhandled promises (no-floating-promises), incorrect async patterns, and any team-specific rules. The --max-warnings 0 flag ensures warnings don't silently accumulate.
Essential plugins for TypeScript:
@typescript-eslint/eslint-plugin— TypeScript-specific ruleseslint-plugin-import— import ordering and resolution@typescript-eslint/no-floating-promises— catches unawaited async calls
Gate 3: Prettier
npx prettier --check src
Consistent formatting prevents style-based review comments and merge conflicts. The --check flag exits with code 1 if any file needs reformatting.
Gate 4: Tests
npx vitest run --coverage
Catches regressions in business logic. Coverage thresholds (--coverage.thresholds.lines=80) prevent coverage from dropping silently.
Testing libraries by use case:
- Unit tests: Vitest (fast, TypeScript-native) or Jest
- Integration tests: Vitest with database fixtures
- E2E tests: Playwright or Cypress
- API tests: Supertest with test database
Gate 5: Security Scanning
npm audit --audit-level=high
Or with Semgrep for deeper static analysis:
npx semgrep --config p/typescript --error
npm audit finds packages with known CVEs. Semgrep detects SQL injection, hardcoded secrets, insecure crypto usage, and other security anti-patterns in your code.
Gate 6: Dependency Error Handling (Nark)
npx nark --tsconfig tsconfig.json
Catches unhandled runtime errors from npm packages. ESLint knows you have a try-catch. Nark knows whether the catch block handles the specific errors the package can throw.
This gate catches bugs that pass all five previous gates:
// Passes tsc, ESLint, Prettier, tests, and security scanning
// nark flags: StripeCardError not handled
try {
const charge = await stripe.charges.create({ amount, currency, source });
return charge;
} catch (error) {
throw new Error('Payment failed'); // card decline == server error, both treated identically
}
Complete GitHub Actions Workflow
# .github/workflows/ci.yml
name: CI
on:
pull_request:
branches: [main, develop]
push:
branches: [main]
jobs:
quality:
name: Quality Gates
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
# Gate 1: TypeScript
- name: TypeScript — type check
run: npx tsc --noEmit
# Gate 2: ESLint
- name: ESLint — lint
run: npx eslint src --ext .ts,.tsx --max-warnings 0
# Gate 3: Prettier
- name: Prettier — format check
run: npx prettier --check src
# Gate 4: Tests
- name: Vitest — unit and integration tests
run: npx vitest run --coverage
# Gate 5: Security
- name: npm audit — dependency vulnerabilities
run: npm audit --audit-level=high
# Gate 6: Dependency error handling
- name: nark — unhandled package errors
run: npx nark --tsconfig tsconfig.json
Checklist: Is Your CI Complete?
Before shipping to production, every PR should pass these checks:
Type safety
-
tsc --noEmitpasses withstrict: truein tsconfig -
strictNullChecks: trueenabled — nullable values require explicit handling -
noUncheckedIndexedAccess: true— array/object access returnsT | undefined
Code quality
- ESLint passes with
--max-warnings 0 -
@typescript-eslint/no-floating-promisesenabled — catches bare async calls -
@typescript-eslint/no-explicit-anyenabled (or warnings tracked)
Formatting
- Prettier check passes — no formatting differences
Tests
- Unit tests pass
- Integration tests pass (with test database)
- Coverage above threshold (recommend ≥80% lines)
Security
-
npm auditclean at high/critical severity - No hardcoded secrets (Semgrep or git-secrets pre-commit hook)
Dependency error handling
-
npx narkpasses — all npm package calls have correct error handling - New packages added to project have profiles checked
Why Most Projects Are Missing Gate 6
The first five gates are well-established in TypeScript CI guides, tutorials, and boilerplate repositories. Gate 6 — dependency error handling — is rarely included because the tool for it only recently became available.
The result is a gap that no other gate covers:
| Bug type | tsc | ESLint | Tests | Nark |
|---|---|---|---|---|
| Wrong argument type | ✅ | |||
| Unawaited promise | ✅ | |||
| Missing test coverage | ✅ | |||
| Unhandled axios network error | ❌ | ❌ | ❌ (unless you test it) | ✅ |
| Missing Prisma P2002 handler | ❌ | ❌ | ❌ (unless you test it) | ✅ |
| Redis without error handler | ❌ | ❌ | ❌ | ✅ |
TypeScript types describe API surfaces. They don't describe runtime errors. Tests cover what you write tests for. Nark covers what the package's profile specifies — independent of whether you thought to test it.
Quick Setup: Add Nark to an Existing CI Pipeline
If you already have a GitHub Actions workflow, add Nark as a single step:
- name: nark — dependency error handling
run: npx nark --tsconfig tsconfig.json
For monorepos with multiple tsconfigs:
- name: nark — scan all packages
run: |
npx nark --tsconfig apps/api/tsconfig.json
npx nark --tsconfig apps/web/tsconfig.json
npx nark --tsconfig packages/shared/tsconfig.json
For projects where you want only errors (not warnings) to fail CI:
- name: nark — scan with error-only threshold
run: npx nark --tsconfig tsconfig.json --min-severity error
Packages Covered by Nark
Nark ships with profiles for 160+ npm packages. The packages most likely to have missing error handling in a typical TypeScript SaaS project:
| Package | Common missing handlers |
|---|---|
| axios | AxiosError network errors, error.response null check |
| @prisma/client | P2002 (unique), P2025 (not found), connection errors |
| stripe | StripeCardError, StripeRateLimitError |
| ioredis | .on('error') handler required |
| pg | Connection errors, query errors |
| @anthropic-ai/sdk | RateLimitError, APIConnectionError |
| openai | RateLimitError, APIConnectionError, AuthenticationError |
| nodemailer | SMTP errors, authentication failures |
Frequently Asked Questions
In what order should CI gates run?
Type checking first (fast, catches most errors). Then linting. Then formatting. Then tests (slowest). Security and Nark can run in parallel with tests. Early-failing fast gates save time when there are basic errors.
Should I run Nark on every commit or only on PRs?
PRs are the right default — that's when you want to block merges. Running on every push to main is useful as a secondary check. No need to run on every local commit (pre-commit hooks slow down development).
What if Nark reports violations on existing code (not new changes)?
Use --min-severity error to only block on errors initially, then clean up warnings over time. Or use Nark's --changed-only mode (available in newer versions) to only check files changed in the PR.
How do I add Nark profiles for packages that aren't in the corpus yet?
The nark-corpus is open source (CC-BY-NC-4.0). You can contribute profiles or file issues to request coverage for specific packages.
Try It Now
npx nark --tsconfig ./tsconfig.json
Add it to your CI pipeline as the sixth quality gate. It catches the class of production errors that type checking, linting, and tests routinely miss.