TypeScript

What TypeScript Checks Before Production—and What It Leaves to Runtime

Learn what TypeScript checks before execution, how compiler settings affect emitted JavaScript, and why runtime inputs still need checks.

Editorial illustration for What TypeScript Checks Before Production—and What It Leaves to Runtime

TypeScript can tell you that a call passes a number where a function expects a string before the application runs. That is useful, but it is not the same as checking every value the application will receive in production. TypeScript checks the code it can analyze; JavaScript still handles the values that arrive at runtime.

A mismatch the checker can catch

Consider a function whose parameter has a type annotation:

function greet(name: string) {
  return `Hello, ${name}`;
}

// Deliberately invalid: this call passes a number where a string is expected.
greet(42);

The annotated parameter lets TypeScript report a type mismatch at the call site without executing greet. This is static type checking: it identifies certain uses of values that do not fit the types established in the code. Fixing this call would resolve that mismatch; it would not prove that every future call receives valid input.

During development, an editor can make such problems visible as you work. VS Code’s TypeScript language service reports errors and warnings inline and in its Problems panel. But VS Code’s language support does not include the tsc compiler. If a project needs tsc to check code or produce JavaScript, the compiler must be installed separately. A project-local installation, such as npm install --save-dev typescript, also avoids possible interactions with other TypeScript projects on the same machine. See the VS Code TypeScript documentation for that distinction. An editor diagnostic is an early signal, not a description of what happened when the program ran.

Choose how much the compiler checks

For a project using tsc to emit JavaScript, this is a small tsconfig.json options excerpt:

{
  "compilerOptions": {
    "strict": true,
    "noEmitOnError": true
  }
}

strict: true turns on TypeScript’s strictness checks together. Two examples explain why that matters. noImplicitAny reports cases where a type implicitly falls back to any, which otherwise gives the checker less information to work with. strictNullChecks makes handling null and undefined more explicit rather than allowing them to be assigned to other types by default. Stricter checking can mean changing code that previously passed the checker, but it provides more validation and more accurate tooling. The Handbook’s discussion of strictness describes this as a dial, not a guarantee that every possible mistake will be found.

The second option addresses a different question: what happens to emitted output when there is an error? In the Handbook’s tsc example, JavaScript is updated by default even though the compiler reports a type error. With noEmitOnError, that output file is not updated. That is a compiler-output decision. Whether a release is blocked also depends on how the release process responds to the error; the option alone does not describe a deployment policy. If a team relies on generated JavaScript, it should check both what the compiler reports and which output its release process would use.

Types disappear; runtime conditions do not

When TypeScript produces JavaScript, type annotations are erased. An annotation such as name: string does not add a JavaScript test to greet, and an assertion is not a substitute for such a test. Type information does not change the program’s runtime behavior. This is the boundary to remember when values come from outside the code the checker can reason about: assigning a type to an expected value does not validate what actually arrived.

A JavaScript condition, on the other hand, runs:

function displayId(id: string | number) {
  if (typeof id === "string") {
    return id.toUpperCase();
  }

  return id.toString();
}

Here typeof id === "string" checks the value at runtime. TypeScript also uses that condition to narrow id to string inside the first branch and number in the other branch. The two effects are related but distinct: JavaScript makes the decision when the function runs, while TypeScript checks which operations the branches permit before it runs. The Handbook’s union example demonstrates the same narrowing.

This function is written for a string | number value; its annotation alone would not establish that an untrusted external value belongs to that union. Check values where they enter the application before relying on their types. A clean type check tells you something valuable about the code under its checking settings—not that external data has passed a runtime test.

Find a note

Search by topic, title, or keyword.