How to check typescript errors

Run npx tsc --noEmit in the project root. It type checks every file the tsconfig includes and writes nothing to disk. A clean project prints no lines and exits 0. A project with a defect prints one line per error, with file, line, column and a TS code, and exits 2.

Why check this

This is the check that runs before a pull request merges and again in CI on every push. It is also the first thing to run after a dependency bump, because a new @types package changes the shape of code nobody touched.

The failure it prevents is a build that ships anyway. Most bundlers erase type annotations during transpilation instead of checking them, so a file with a type error can reach the output and fail at runtime. The compiler is the one tool in the chain that reads the types, and --noEmit asks it to read them without producing files.

Prerequisites

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noEmit": true
  },
  "include": ["src"]
}
// src/cart.ts
export interface Line {
  sku: string;
  qty: number;
}

export function firstSku(lines: Line[]): string {
  return lines[0].sku;
}
// src/total.ts
import type { Line } from './cart.js';

export function total(lines: Line[], rate: string): number {
  return lines.reduce((sum, l) => sum + l.qty, 0) * rate;
}
// legacy/report.ts, outside the include pattern on purpose
export function label(id: number): string {
  return id;
}

Steps

  1. Step 1.

    Read the compiler version the project resolves to, not the one on your PATH.

    npx tsc --version
    
    Version 5.9.2

    Error text and error codes change between minor versions. Record this number with any result you report.

  2. Step 2.

    Type check the whole project and read the exit code.

    npx tsc --noEmit --pretty false; echo "exit=$?"
    
    src/cart.ts(7,10): error TS2532: Object is possibly 'undefined'.
    src/total.ts(4,53): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
    exit=2

    --pretty false gives one error per line with no colour codes, which is the form a CI log and a grep can both read.

  3. Step 3.

    List the files the project actually checks. This is the answer to most "it passes for me" reports.

    npx tsc --showConfig
    
    {
      "compilerOptions": {
          "target": "es2022",
          "module": "esnext",
          "moduleResolution": "bundler",
          "strict": true,
          "noUncheckedIndexedAccess": true,
          "noEmit": true,
          …
      },
      "files": [
          "./src/cart.ts",
          "./src/total.ts"
      ],
      "include": [
          "src"
      ]
    }

    files is the resolved list. legacy/report.ts is not in it, so its obvious defect is invisible to step 2 and always will be.

  4. Step 4.

    Check one file by naming it on the command line, and watch the result change.

    npx tsc --noEmit src/cart.ts; echo "exit=$?"
    
    exit=0

    The same file errored in step 2. A file path argument makes tsc ignore tsconfig.json completely, so this ran with compiler defaults, where strict is off.

  5. Step 5.

    Separate the two errors by the option that causes one of them.

    npx tsc --noEmit --pretty false --noUncheckedIndexedAccess false; echo "exit=$?"
    
    src/total.ts(4,53): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
    exit=2

    lines[0].sku is an error only while noUncheckedIndexedAccess is on, because that option gives every index read a | undefined. One flag, and TS2532 is gone.

How to read the result

| What you see | What it means | What to do | | --- | --- | --- | | No output, exit 0 | Every file in files type checks | Nothing. Record the tsc version beside the result. | | error TS2532: Object is possibly 'undefined' | An index or optional read with no guard | Guard it, or narrow with a length check. Do not reach for !. | | error TS2363 on a number operation | An operand is not a number, often a string from config or a query string | Fix the type at the boundary where the value enters. | | Errors locally, none in CI | The two runs resolved different tsconfig files or different tsc versions | Compare npx tsc --showConfig and npx tsc --version on both. | | exit 2 with no error lines | The compiler could not start, usually a missing or malformed tsconfig | Read stderr. Configuration failures use the same exit code as type errors. |

Common mistakes

Sign: tsc is clean on a file the editor marks red, or the other way round.Cause: The editor checks the open file inside the project it belongs to. `tsc` checks the list in `files`, and the moment you pass a path argument it stops reading tsconfig.json at all. Step 4 is that gap: `npx tsc --noEmit src/cart.ts` exits 0 on a file the project run reports as TS2532, because the argument form falls back to compiler defaults with `strict` off.
Sign: Code that compiles on one machine fails on another with TS2532 or TS18048, with no code change between them.Cause: `noUncheckedIndexedAccess` is not part of `strict`, so it is set per project and a fork or an older branch often lacks it. With the option on, `lines[0]` has type `Line | undefined` and every array read needs a guard. Step 5 turns it off and one of the two errors disappears.
Sign: A CI step that tests for exit code 1 reports the type check as passed.Cause: tsc exits 2 when it finds type errors, not 1. A shell conditional written as `if [ $? -eq 1 ]` never fires. Test for a non-zero status, or let the step fail on its own.
Sign: A whole directory has no errors and never had any.Cause: It is outside the `include` pattern. `legacy/report.ts` in the project above returns a `number` from a function declared to return `string` and no project run mentions it. `--showConfig` prints the resolved `files` array, which is the only reliable way to see what is being checked.

What to check next

FAQ

How do I check types without building?

npx tsc --noEmit does exactly that. It runs the full program check and writes no .js, no .d.ts and no source maps. The exit code is the result: 0 for clean, 2 for type errors.

Why does my editor show an error that tsc does not?

Usually the file sits outside the include pattern, so the project run never reads it. Run npx tsc --showConfig and look for the path in the files array. If it is absent, widen include or add the directory to a second tsconfig.

What exit code does tsc return?

0 with no errors and 2 with type errors, as step 2 shows. Configuration failures also exit 2. Treat any non-zero status as a failure rather than matching on 1.

How do I check a single file with the project settings?

You cannot do it with a path argument, because that disables tsconfig.json. Keep the project run and filter its output, or point --project at a second tsconfig whose include names only that file.

Should the type check run before or after the tests?

Before. It takes seconds and it fails on code the test run would execute anyway. A failing type check makes most of the test output noise.

Verified

Verified by Maks Vernytsc 5.9.2node 22.23.2

Each output block is what the command above it printed on that date, on the host named in the step. Figures read from a live site move between runs. Compare the shape of the answer rather than the digits, and see the methodology for how a page is re-verified.

basic6 minpublished updated Maks Verny