TypeScript path · The compiler · Topic 1 of 11

What the compiler checks, then erases

Not started

TypeScript is JavaScript plus type annotations. The compiler reads the annotations, reports anything that doesn't fit together, and then deletes them. What runs in the browser or in Node is plain JavaScript that behaves as if the types had never been written. At runtime nothing knows what a User is, and nothing checks that a value is one.

Why it matters: Almost every TypeScript surprise (a crash in typed code, an `instanceof` that won't compile, a bundler that ignores your errors) comes from forgetting that the types never reach runtime.

  1. 1Two separate jobs

    The compiler checks your types and, separately, writes out JavaScript. A type error does not stop the JavaScript from being written unless you ask it to.

  2. 2Types are erased

    Annotations, interfaces, type aliases, generics and `as` casts are deleted. The output behaves exactly as if you had never written them.

  3. 3A few things are not just types

    Enums, namespaces and constructor parameter properties produce real JavaScript. The `erasableSyntaxOnly` flag forbids them, so that removing the types is all a tool has to do.

1

How it works

The explanation, with examples you can run.

Check, then emit

tsc does two independent jobs: it type-checks the program, and it writes JavaScript. By default it writes the JavaScript even when there are type errors (noEmitOnError is off), because the errors are about your intent, not about whether the code can run. Many tools only do the second job (see the build topic), which is why the type check has to run somewhere on its own, in your editor and in CI.

What the output looks like

interface User {  id: number  name: string} function greet(user: User): string {  return `Hello, ${user.name}`} const ada = { id: 1, name: "Ada" } as Userconsole.log(greet(ada))

Compiled with tsc --target es2022, this becomes:

"use strict";function greet(user) {    return `Hello, ${user.name}`;}const ada = { id: 1, name: "Ada" };console.log(greet(ada));

The interface is gone, user: User is just user, and the as User cast has vanished. A cast never converts or checks anything: it only tells the checker to believe you.

Two consequences follow:

  • You cannot ask about a type at runtime. instanceof User does not compile when User is an interface, and typeof x === "User" is never true. To branch on data at runtime you need something that exists at runtime: a typeof result, a property you test with in, a class, or a tag field (see discriminated unions).
  • Types cannot change behaviour. Two programs that differ only in their types compile to the same JavaScript. If the program crashes, the types were wrong about the data, not the other way round.

The exceptions

A few TypeScript features are not just types, so deleting them would break the program:

  • enum becomes an object (with reverse mappings for numeric members).
  • namespace with values becomes a function wrapper.
  • Constructor parameter properties (constructor(private name: string)) become assignments.

TypeScript 5.8 added erasableSyntaxOnly, which reports these as errors. It exists because tools now run TypeScript by blanking out the types and nothing else. Node.js does this since 22.18 and 23.6: node app.ts replaces the type syntax with spaces and runs the result, without type-checking it, and it refuses an enum with ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX. A union of string literals (type Role = "admin" | "reader") or a plain as const object usually does the job of an enum without generating code.

Downlevelling is a separate question

target decides which JavaScript syntax the output may use. With an old target, tsc rewrites newer syntax (optional chaining, class fields) into older equivalents. That rewriting is about JavaScript versions, not types, and many projects leave it to a bundler instead.

2

Check yourself

3 questions. Get them all right to finish this topic.

  1. 1.`User` is an interface. Why does `if (value instanceof User)` fail to compile?
  2. 2.`tsc` reports a type error in one file, with the default settings. What happens to that file's JavaScript?
  3. 3.Which of these produces JavaScript code of its own?

0 of 3 right so far. Get all three to finish this topic.