Skip to content
Theory · First steps · lesson 6 of 7

Types vanish at compile time

At customs, declarations are needed only at the border. Once the parcels are checked, the papers are thrown away, and inside the country nobody reads them again. tsc does the same: it first checks the types, then writes JavaScript that has none of them. This is called type erasure.

Let's see what tsc leaves of a file hello.ts with annotations. Press "Next step" and watch what each highlighted TypeScript line turns into.

LiveWhat tsc leaves of a filestep 1 of 5

The parameter and the result had : string annotations; the JavaScript has none, and the function stays.

The highlight shows a TypeScript line and the JavaScript line it became

What is left in hello.js

After compilation, in hello.js:

  • all the annotations are gone: : string on the parameter and the result, : string and : number on the variables;
  • everything that runs is still there: the function, the template literal, the values "Anna" and 3, the console.log call;
  • a line "use strict"; appeared at the top: tsc added it. That is JavaScript's own strict mode, not tsc's, and it has nothing to do with types.

The result is plain JavaScript without a word about types. The command node hello.js runs it like any other .js file.

Why erase the types

Why not keep the annotations in the JavaScript? Because they are not JavaScript. When a browser or Node.js meets : string in a .js file, it stops with a syntax error before the first line runs. That is why tsc cuts the types out: without them the file makes sense to both the browser and Node.js.

No types at run time

Since hello.js has no types, nobody checks them while the program runs. There was one check, before the run, from the program's text. That is why types do not protect you from data that appears only while the program runs:

  • text a user typed into a form;
  • a server's response;
  • the contents of a file.

In the customs picture such data enters the country around the border, with no declaration: tsc has never seen it. If a value needs checking, the program does it itself with JavaScript, for example with typeof.

Interviewers often ask whether TypeScript types exist at run time. They do not: tsc erases them at compile time, and the running program is JavaScript alone.

An error does not stop the file

Here the customs picture stops working. A real inspector holds back a parcel with a wrong declaration, while tsc reports the error and writes the .js anyway. Checking types and writing JavaScript are two separate jobs, and the second does not wait for the first to succeed.

To keep tsc from writing the file while the code has errors, you need the --noEmitOnError flag:

TEXT
$ tsc --noEmitOnError hello.ts

With it, no new hello.js appears when there is an error. An old one, if it is left from an earlier compilation, stays as it was.

Common mistake

Seeing a tsc message and running hello.js anyway. Let's go back to the function from the first lesson of this theme:

TypeScript
function formatPrice(price: number): string {
    return price.toFixed(2);
}

console.log(formatPrice(100));
console.log(formatPrice("100")); // ✗
TEXT
error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.

tsc hello.ts reports this error but writes hello.js all the same. The command node hello.js prints 100.00, and on the second call the program stops:

TEXT
TypeError: price.toFixed is not a function

The types are erased, and the string "100" reached toFixed as in JavaScript without any TypeScript. The fix is to clear the errors tsc reported first, and only then run the program.

Key points

  • tsc first checks the types, then writes JavaScript without them: this is type erasure.
  • hello.js has no annotations, so at run time types check nothing.
  • tsc never saw data that arrives while the program runs: it is checked with JavaScript, for example typeof.
  • tsc writes the .js even when there are errors, and the --noEmitOnError flag forbids that.