Skip to content
The Switch · JavaScript to TypeScript · Lesson 12

The cheat sheet

Every pattern in the course, on one page you can print.

Contents

This page is for later. You have been through the lessons, and now you want the answer to "how do I write that again?" in ten seconds.

Each row shows the JavaScript you know on the left and the TypeScript it becomes on the right, with the lines that differ tinted. Every row is real code that was type-checked and tested, like the lessons. Each row names the lesson that explains it, so you can go back for the reasons.

Two lessons have no rows, because they have no code to compare. Lesson 0 is the honest list of what you gain and what it costs. Lesson 1 is setup. These are the commands from it.

You want toType this
Add TypeScript to a projectnpm i -D typescript
Make a tsconfig.jsonnpx tsc --init
Check without producing filesnpx tsc --noEmit
Run a .ts file with Node's type strippingnode file.ts

Use the contents list to jump between groups. To keep a copy, print the page or save it as a PDF. The header, the sidebar and the buttons are left out of the printout.

Part 1 of 10 · Basics

Start here

Setup, labels, and letting TypeScript work it out.

Label a variable

Lesson 2: Your first types
JavaScript
const city = "Pune";let seats = 40;let isOpen = true;
TypeScriptPlayground (opens in a new tab)
const city: string = "Pune";let seats: number = 40;let isOpen: boolean = true;

A colon and a type. You rarely need to, because TypeScript works the type out from the value.

Let inference do it

Lesson 2: Your first types
JavaScript
const price = 10;const tag = "Rs " + price;
TypeScriptPlayground (opens in a new tab)
const price = 10; // numberconst tag = "Rs " + price; // string

No label needed when the value says it all. The comments show what TypeScript worked out.

A variable keeps its type

Lesson 2: Your first types
JavaScript
let age = 21;age = "twenty-one";console.log(age + 1);
TypeScriptPlayground (opens in a new tab)
let age = 21;age = "twenty-one";console.log(age + 1);

TypeScript says

error TS2322: Type 'string' is not assignable to type 'number'.

The first value decides the type, so a later value of another kind is refused.

Part 2 of 10 · Objects, arrays, tuples

Objects, arrays, tuples

The shape of your data, written once.

Name an object's shape

Lesson 3: Objects
JavaScript
const user = { name: "Asha", age: 21 };
TypeScriptPlayground (opens in a new tab)
type User = { name: string; age: number };const user: User = { name: "Asha", age: 21 };

A type lists the properties and their kinds. An interface works too.

Optional and read-only

Lesson 3: Objects
JavaScript
const contact = { id: 1 };
TypeScriptPlayground (opens in a new tab)
type Contact = { readonly id: number; email?: string };const contact: Contact = { id: 1 };

? means may be missing. readonly means set once, never changed.

Changing a read-only property

Lesson 3: Objects
JavaScript
const config = { host: "localhost", port: 8080 };config.port = 9090;console.log(config.port);
TypeScriptPlayground (opens in a new tab)
type Config = {  readonly host: string;  readonly port: number;};const config: Config = { host: "localhost", port: 8080 };config.port = 9090;console.log(config.port);

TypeScript says

error TS2540: Cannot assign to 'port' because it is a read-only property.

The checker refuses the assignment. It is a promise to your teammates, and it is gone when the code runs.

Part 3 of 10 · Functions

Functions, labelled

Inputs, outputs, defaults and callbacks.

Label inputs and the result

Lesson 4: Arrays, tuples and functions
JavaScript
function total(prices) {  return prices.reduce((sum, price) => sum + price, 0);}
TypeScriptPlayground (opens in a new tab)
function total(prices: number[]): number {  return prices.reduce((sum, price) => sum + price, 0);}

Label the parameters. The return type can be left to inference, but many teams write it for public functions.

Default and optional parameters

Lesson 4: Arrays, tuples and functions
JavaScript
function greet(person, greeting = "Hello", suffix) {  return greeting + ", " + person + (suffix ?? "");}
TypeScriptPlayground (opens in a new tab)
function greet(person: string, greeting = "Hello", suffix?: string): string {  return greeting + ", " + person + (suffix ?? "");}

A default value gives the type for free. A ? parameter may be undefined inside.

Describe a function

Lesson 4: Arrays, tuples and functions
JavaScript
const apply = (n, change) => change(n);
TypeScriptPlayground (opens in a new tab)
type Change = (n: number) => number;const apply = (n: number, change: Change): number => change(n);

(n: number) => number reads: takes a number, gives a number. Name it with type to reuse it.

Part 4 of 10 · Unions and narrowing

Unions and narrowing

One of several, and how to find out which.

Text or a number

Lesson 5: Unions and narrowing
JavaScript
function label(id) {  return typeof id === "string" ? id.toUpperCase() : "No. " + id;}
TypeScriptPlayground (opens in a new tab)
function label(id: string | number): string {  return typeof id === "string" ? id.toUpperCase() : "No. " + id;}

A | B means one of them. Check with typeof before using what only one has.

Has this property?

Lesson 5: Unions and narrowing
JavaScript
function score(player) {  return "runs" in player ? player.runs : player.wickets;}
TypeScriptPlayground (opens in a new tab)
type Batter = { runs: number };type Bowler = { wickets: number };function score(player: Batter | Bowler): number {  return "runs" in player ? player.runs : player.wickets;}

The in operator tells TypeScript which member of the union you hold.

A kind field

Lesson 5: Unions and narrowing
JavaScript
function serve(order) {  switch (order.kind) {    case "chai":      return order.sugar + " sugars";    case "coffee":      return order.strong ? "strong" : "mild";  }}
TypeScriptPlayground (opens in a new tab)
type Order = { kind: "chai"; sugar: number } | { kind: "coffee"; strong: boolean };function serve(order: Order): string {  switch (order.kind) {    case "chai":      return order.sugar + " sugars";    case "coffee":      return order.strong ? "strong" : "mild";  }}

Give every member a kind with an exact word. A switch on it narrows to one member.

Check every case was handled

Lesson 5: Unions and narrowing
JavaScript
function price(drink) {  switch (drink) {    case "chai":      return 10;    case "coffee":      return 20;    default:      return undefined;  }}
TypeScriptPlayground (opens in a new tab)
type Drink = "chai" | "coffee";function assertNever(value: never): never {  throw new Error("Unhandled: " + value);}function price(drink: Drink): number {  switch (drink) {    case "chai":      return 10;    case "coffee":      return 20;    default:      return assertNever(drink);  }}

In default, pass the value to a function that takes never. A missing case becomes an error.

A few exact words

Lesson 5: Unions and narrowing
JavaScript
function brew(strength) {  return strength === "strong" ? "Two shots" : "One shot";}
TypeScriptPlayground (opens in a new tab)
type Strength = "mild" | "strong";function brew(strength: Strength): string {  return strength === "strong" ? "Two shots" : "One shot";}

A union of words works like a small enum, with nothing added to your JavaScript.

A word that is not on the list

Lesson 5: Unions and narrowing
JavaScript
function brew(strength) {  return strength === "strong" ? "Two shots" : "One shot";}console.log(brew("strong"));console.log(brew("medium"));
TypeScriptPlayground (opens in a new tab)
type Strength = "mild" | "strong";function brew(strength: Strength): string {  return strength === "strong" ? "Two shots" : "One shot";}console.log(brew("strong"));console.log(brew("medium"));

TypeScript says

error TS2345: Argument of type '"medium"' is not assignable to parameter of type 'Strength'.

The editor offers the allowed words as you type, and refuses the rest.

Part 5 of 10 · Missing values

Missing values

null and undefined, handled before they bite.

Check what find returns

Lesson 6: null, undefined and strictness
JavaScript
function nameOf(students, roll) {  const found = students.find((s) => s.roll === roll);  return found === undefined ? "No such student" : found.name;}
TypeScriptPlayground (opens in a new tab)
type Student = { roll: number; name: string };function nameOf(students: Student[], roll: number): string {  const found = students.find((s) => s.roll === roll);  return found === undefined ? "No such student" : found.name;}

find gives the item or undefined. Look first, then use.

Reaching into a maybe

Lesson 6: null, undefined and strictness
JavaScript
const students = [{ roll: 1, name: "Asha" }];function nameOf(roll) {  return students.find((s) => s.roll === roll)?.name;}console.log(nameOf(1));console.log(nameOf(9));
TypeScriptPlayground (opens in a new tab)
type Student = { roll: number; name: string };const students: Student[] = [  { roll: 1, name: "Asha" },  { roll: 2, name: "Ravi" },];function nameOf(roll: number): string {  return students.find((s) => s.roll === roll).name;}console.log(nameOf(1));console.log(nameOf(9));

TypeScript says

error TS2532: Object is possibly 'undefined'.

The checker stops you at the dot, where JavaScript crashes later.

Stop at the first empty box

Lesson 6: null, undefined and strictness
JavaScript
function cityOf(customer) {  return customer.address?.city;}
TypeScriptPlayground (opens in a new tab)
type Customer = { address?: { city: string } };function cityOf(customer: Customer): string | undefined {  return customer.address?.city;}

?. gives undefined instead of crashing. The result type says it may be missing.

Fallback for missing, not for zero

Lesson 6: null, undefined and strictness
JavaScript
function runsLabel(runs) {  return "Runs: " + (runs || "did not bat");}
TypeScriptPlayground (opens in a new tab)
function runsLabel(runs: number | undefined): string {  return "Runs: " + (runs ?? "did not bat");}

?? replaces only null and undefined. || also replaces 0, empty text and false.

The bang, with a proof

Lesson 6: null, undefined and strictness
JavaScript
const seats = new Map([["A", 1]]);const next = seats.get("A") + 1;
TypeScriptPlayground (opens in a new tab)
const seats = new Map<string, number>([["A", 1]]);// Only when the line above proves it is there.const next = seats.get("A")! + 1;

! is a promise. Use it only when the line above proves the value is there.

Part 6 of 10 · Generics

Generics, just enough

One function for many types.

Limit the blank

Lesson 7: Generics, just enough
JavaScript
function longest(a, b) {  return a.length >= b.length ? a : b;}console.log(longest("chai", "coffee"));console.log(longest(10, 20));
TypeScriptPlayground (opens in a new tab)
function longest<T extends { length: number }>(a: T, b: T): T {  return a.length >= b.length ? a : b;}console.log(longest("chai", "coffee"));console.log(longest(10, 20));

TypeScript says

error TS2345: Argument of type 'number' is not assignable to parameter of type '{ length: number; }'.

T extends { length: number } means any type that has a length.

A type with a blank

Lesson 7: Generics, just enough
JavaScript
const lucky = { value: 7 };const note = { value: "Call Amma" };
TypeScriptPlayground (opens in a new tab)
type Box<T> = { value: T };const lucky: Box<number> = { value: 7 };const note: Box<string> = { value: "Call Amma" };

Box<number> fills the blank. Array<T>, Map<K, V> and Promise<T> work the same way.

Part 7 of 10 · Utility types

Utility types

Reshape a type you already have.

Partial: every property optional

Lesson 8: Utility types
JavaScript
function updateUser(user, changes) {  return { ...user, ...changes };}const asha = { id: 1, name: "Asha", email: "asha@example.com", password: "chai123" };const renamed = updateUser(asha, { nmae: "Asha Rao" });console.log(renamed.name);
TypeScriptPlayground (opens in a new tab)
type User = { id: number; name: string; email: string; password: string };function updateUser(user: User, changes: Partial<User>): User {  return { ...user, ...changes };}const asha: User = { id: 1, name: "Asha", email: "asha@example.com", password: "chai123" };const renamed = updateUser(asha, { nmae: "Asha Rao" });console.log(renamed.name);

TypeScript says

error TS2353: Object literal may only specify known properties, and 'nmae' does not exist in type 'Partial<User>'.

Good for update functions. A misspelled key is refused.

Pick and Omit

Lesson 8: Utility types
JavaScript
const user = { id: 1, name: "Asha", password: "chai123" };const preview = { id: user.id, name: user.name };const { password, ...publicUser } = user;
TypeScriptPlayground (opens in a new tab)
type User = { id: number; name: string; password: string };type Preview = Pick<User, "id" | "name">;type PublicUser = Omit<User, "password">;

Pick keeps the named properties. Omit removes them, and does not check the spelling of its keys.

Record: a value for every key

Lesson 8: Utility types
JavaScript
const prices = { chai: 10, coffee: 20 };
TypeScriptPlayground (opens in a new tab)
type Drink = "chai" | "coffee";const prices: Record<Drink, number> = { chai: 10, coffee: 20 };

With a union of words as the key, none may be forgotten.

ReturnType: borrow a result type

Lesson 8: Utility types
JavaScript
function makeTicket(seat) {  return { seat, paid: false };}
TypeScriptPlayground (opens in a new tab)
function makeTicket(seat: number) {  return { seat, paid: false };}type Ticket = ReturnType<typeof makeTicket>;

typeof in a type position means the type of that value. Also see Required and Readonly.

Part 8 of 10 · Data from outside

Data from outside

Check the parcel at the door.

unknown must be checked

Lesson 9: Data from outside
JavaScript
function shout(value) {  return value.toUpperCase();}console.log(shout("chai"));
TypeScriptPlayground (opens in a new tab)
function shout(value: unknown): string {  return value.toUpperCase();}

TypeScript says

error TS18046: 'value' is of type 'unknown'.

any lets everything through. unknown waits for a check.

Narrow an unknown

Lesson 9: Data from outside
JavaScript
function describe(value) {  return typeof value === "string" ? value.toUpperCase() : "not text";}
TypeScriptPlayground (opens in a new tab)
function describe(value: unknown): string {  return typeof value === "string" ? value.toUpperCase() : "not text";}

A typeof check turns unknown into something you can use.

A type guard

Lesson 9: Data from outside
JavaScript
function isTicket(value) {  return (    typeof value === "object" &&    value !== null &&    typeof value.coach === "string" &&    typeof value.seat === "number"  );}
TypeScriptPlayground (opens in a new tab)
type Ticket = { coach: string; seat: number };function isTicket(value: unknown): value is Ticket {  return (    typeof value === "object" &&    value !== null &&    "coach" in value &&    typeof value.coach === "string" &&    "seat" in value &&    typeof value.seat === "number"  );}

value is Ticket tells TypeScript what a true answer means. A guard that lies is worse than none.

Check what fetch gives back

Lesson 9: Data from outside
JavaScript
async function loadScores(url) {  const response = await fetch(url);  return response.json();}
TypeScriptPlayground (opens in a new tab)
function isNumberList(value: unknown): value is number[] {  return Array.isArray(value) && value.every((item) => typeof item === "number");}async function loadScores(url: string): Promise<number[]> {  const response = await fetch(url);  const data: unknown = await response.json();  return isNumberList(data) ? data : [];}

response.json() is any. Put it in unknown and check it before use.

Part 9 of 10 · Migrating

Migrating, step by step

Let it in, check a little, convert a file.

Rename a file, add types

Lesson 10: Migrating a real project
JavaScript
function lineTotal(price, quantity) {  return price * quantity;}
TypeScriptPlayground (opens in a new tab)
function lineTotal(price: number, quantity: number): number {  return price * quantity;}

Rename .js to .ts, then label the parameters. Climb the strictness ladder one rung at a time.

A known gap, written down

Lesson 10: Migrating a real project
JavaScript
function legacyTotal(prices) {  return prices.reduce((sum, price) => sum + Number(price), 0);}legacyTotal("10,20".split(","));
TypeScriptPlayground (opens in a new tab)
function legacyTotal(prices: number[]): number {  return prices.reduce((sum, price) => sum + Number(price), 0);}// @ts-expect-error an old caller passes text. Fix it in the next change.legacyTotal("10,20".split(","));

@ts-expect-error says the next line is known to fail, and complains when it stops failing.

Part 10 of 10 · Escape hatches

Escape hatches

Passes that skip the checker, not the crash.

A key that is not there

Lesson 11: Traps and escape hatches
JavaScript
const prices = { chai: 10, coffee: 20 };console.log(prices.chai);console.log(prices.lassi);
TypeScriptPlayground (opens in a new tab)
type Prices = Record<string, number>;const prices = { chai: 10, coffee: 20 } satisfies Prices;console.log(prices.chai);console.log(prices.lassi);

TypeScript says

error TS2339: Property 'lassi' does not exist on type '{ chai: number; coffee: number; }'.

The exact keys are remembered, so a missing one is an error.

Check instead of cast

Lesson 11: Traps and escape hatches
JavaScript
const data = "12";const text = typeof data === "string" ? data : "";const price = Number(text);
TypeScriptPlayground (opens in a new tab)
const data: unknown = "12";const text = typeof data === "string" ? data : "";const price = Number(text);

A check proves the kind. A cast only claims it.

A cast between strangers

Lesson 11: Traps and escape hatches
JavaScript
const price = "12";
TypeScriptPlayground (opens in a new tab)
const price = "12" as number;

TypeScript says

error TS2352: Conversion of type 'string' to type 'number' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.

Even as is refused between unrelated types. Going through unknown forges the pass, so avoid it.

Updated 2026-10-17

Made by VeraStack Labs