Introduction

TypeScript adds a type system on top of JavaScript, then compiles down to plain JavaScript that runs anywhere JavaScript already does. The types exist purely to help you and your editor catch mistakes before the code runs — by the time it's compiled, every type annotation is gone.

A plain JavaScript problem

In JavaScript, nothing stops you from calling a function with the wrong kind of value. The mistake only shows up once the code actually runs:

JS greet.js
function greet(name) {
  return "Hello, " + name.toUpperCase() + "!";
}

console.log(greet(42));
Output
Uncaught TypeError: name.toUpperCase is not a function

Nothing about the code above looked wrong until it ran — 42 is a valid argument as far as JavaScript is concerned, right up until .toUpperCase() is called on it.

The same code in TypeScript

Adding a type annotation to the parameter tells the compiler exactly what's allowed, so the mistake is caught before the program ever runs:

TS greet.ts
function greet(name: string) {
  return "Hello, " + name.toUpperCase() + "!";
}

console.log(greet(42));
tsc output
greet.ts:5:19 - error TS2345: Argument of type 'number' is not
assignable to parameter of type 'string'.

5 console.log(greet(42));
                    ~~

name: string is a type annotation — it tells the compiler that name must always be a string. Running tsc greet.ts catches the mismatch immediately and refuses to produce output until it's fixed, instead of letting it become a runtime crash.

Compiling to JavaScript

Once the types check out, tsc strips every type annotation and produces ordinary JavaScript:

TS greet.ts (fixed)
function greet(name: string): string {
  return "Hello, " + name.toUpperCase() + "!";
}

console.log(greet("maya"));
Compiles to greet.js
function greet(name) {
  return "Hello, " + name.toUpperCase() + "!";
}
console.log(greet("maya"));
Running greet.js
Hello, MAYA!
Note: TypeScript's types are completely erased at compile time — the JavaScript that actually runs contains no trace of them. This means a type error can never happen "at runtime" the way a null pointer exception can; if tsc compiles your code successfully, that particular category of mistake is already ruled out. It also means TypeScript can't catch a type mismatch coming from outside your code, like a value from an API response — you're on your own for validating that.