You have written JavaScript for ten years. You know closures, you know why this betrays you
inside a regular function, you have debugged a production issue at midnight with nothing but
console.log and spite. TypeScript is not a new language asking you to start over. It is the
JavaScript you already write, plus a very literal-minded assistant standing just off camera,
holding up cue cards that say "that is not a string, Steve" a few seconds before your code would
have said the same thing to your users, live, on air.
Types are erased
Before any syntax, one idea makes everything else make sense: TypeScript's types do not exist once your code runs. They are a layer you add for the compiler and your editor, and then that layer gets stripped out entirely before the code ships. Here is the whole trick, side by side.
What you write:
function greet(name: string): string {
return `Welcome to the house, ${name}.`
}What actually ships to the browser:
function greet(name) {
return `Welcome to the house, ${name}.`;
}Every colon, every string, gone. That erasure has three consequences worth sitting with before
you write a single annotation:
- Zero runtime cost. No extra bytes, no type-checking code bundled into production. The types never make it past your build step.
- Zero runtime protection. If bad data sneaks in after compile time (an API response, a
JSON.parse, a form field somebody typed a number into), TypeScript is not standing there checking it. It already left the building. - All the value is at compile time. Your editor,
npx tsc, your build. That is the entire show. Think of it as a dress rehearsal that catches every flub before the cameras are live.
Annotations on variables and parameters
The core move is small: a colon, then a type, after a name. On a variable it looks like
const week: number = 1. On a function parameter, same idea, and the function itself can
declare its return type too. Here is an untyped houseguest helper next to its typed twin.
The ES6 you write
function announceHouseguest(name, age, hometown) {
return `${name}, age ${age}, straight outta ${hometown}.`
}The TS version
function announceHouseguest(name: string, age: number, hometown: string): string {
return `${name}, age ${age}, straight outta ${hometown}.`
}What changed
Every parameter gets a colon and a type: name: string, age: number, hometown: string. If you have ever written PHP 7 type hints (function foo(string $name, int $age)), this is the same instinct, just living after the parameter name instead of before it. The colon after the closing parenthesis, : string, types the return value the same way.
Nothing about the logic changed. The function still takes three values and hands back a string.
What changed is that TypeScript now knows, without running a single line, exactly what shape
goes in and what shape comes out. Call announceHouseguest('Chelsie', 'thirty three', 'Chicago')
by mistake and it will not wait for a bug report. It tells you at the keyboard.
Arrays and return types
Arrays get the same treatment: the element type plus square brackets. string[] is an array of
strings, number[] is an array of numbers. Combine that with a return type and you get a
function whose entire contract is readable in one line, no need to scroll to find out what it
hands back.
The ES6 you write
function nomineesRemaining(nominees) {
return nominees.length
}The TS version
function nomineesRemaining(nominees: string[]): number {
return nominees.length
}What changed
The parameter goes from a bare name to nominees: string[], an array of strings. The return type after the parenthesis, : number, promises this function always hands back a number, never undefined, never a string it forgot to convert.
That is the whole lesson. Now prove it in the comp below.