TypeScript Cheat Sheet
Types, interfaces, unions and narrowing, functions, classes, generics, utility and mapped types, assertions and tsconfig.
Updated
Basic types and annotations#
let title: string = "TypeScript";
let lessons: number = 39;
let published: boolean = true;
let tags: string[] = ["types", "compiler"];
let point: [number, number] = [3, 4]; // tuple
let id: string | number = 42; // union
let size: "sm" | "md" | "lg" = "md"; // literal union
let maybe: string | null = null;
let data: unknown = JSON.parse("{}"); // must narrow before use
function log(message: string): void {
console.log(message);
}
function fail(message: string): never {
throw new Error(message);
}Prefer inference for initialised variables; always annotate parameters.
Object types, aliases and interfaces#
type User = {
readonly id: number;
name: string;
email?: string; // optional
};
interface Product {
sku: string;
price: number;
describe(): string;
}
interface DigitalProduct extends Product {
downloadUrl: string;
}
type WithTimestamps = { createdAt: Date };
type StoredUser = User & WithTimestamps; // intersection
type Prices = Record<string, number>; // dictionary
type Scores = { [subject: string]: number }; // index signatureFunctions#
function greet(name: string, title?: string, greeting = "Hello"): string {
return title ? `${greeting}, ${title} ${name}` : `${greeting}, ${name}`;
}
const sum = (...nums: number[]): number => nums.reduce((a, b) => a + b, 0);
type Formatter = (value: number) => string;
const rupees: Formatter = (v) => `₹${v.toFixed(2)}`;
function parse(value: string): number;
function parse(value: string[]): number[];
function parse(value: string | string[]): number | number[] {
return Array.isArray(value) ? value.map(Number) : Number(value);
}Narrowing#
function describe(value: string | number | Date | null): string {
if (value === null) return "nothing";
if (typeof value === "string") return value.toUpperCase();
if (value instanceof Date) return value.toISOString();
return value.toFixed(2);
}
type Shape = { kind: "circle"; r: number } | { kind: "square"; side: number };
function area(s: Shape): number {
switch (s.kind) {
case "circle":
return Math.PI * s.r ** 2;
case "square":
return s.side ** 2;
default: {
const unhandled: never = s; // exhaustive check
return unhandled;
}
}
}
function isUser(v: unknown): v is { name: string } {
return typeof v === "object" && v !== null && typeof (v as { name?: unknown }).name === "string";
}Classes#
class Account {
#balance = 0;
static readonly BANK = "Laviyx Bank";
constructor(public readonly owner: string, private limit = 1000) {}
deposit(amount: number): void {
this.#balance += amount;
}
get balance(): number {
return this.#balance;
}
}
abstract class Shape2 {
abstract area(): number;
describe(): string {
return `area ${this.area()}`;
}
}
interface Notifier {
send(to: string): void;
}
class EmailNotifier implements Notifier {
send(to: string): void {
console.log(`email to ${to}`);
}
}Generics#
function first<T>(items: readonly T[]): T | undefined {
return items[0];
}
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
interface ApiResponse<T = unknown> {
ok: boolean;
data: T;
}
class Box<T> {
constructor(public value: T) {}
}Type operators and utility types#
const ROUTES = { home: "/", courses: "/courses" } as const;
type RouteName = keyof typeof ROUTES; // "home" | "courses"
type RoutePath = (typeof ROUTES)[RouteName]; // "/" | "/courses"
interface Person {
id: number;
name: string;
email: string;
}
type Update = Partial<Omit<Person, "id">>;
type Preview = Pick<Person, "id" | "name">;
type Frozen = Readonly<Person>;
type Visible = Exclude<"draft" | "live" | "archived", "draft">;
type Loaded = Awaited<Promise<string[]>>;
type Ret = ReturnType<() => { ok: boolean }>;
type Optional<T> = { [K in keyof T]?: T[K] }; // mapped type
type ElementOf<T> = T extends (infer E)[] ? E : never; // conditional + infer
type EventName = `on${Capitalize<"click" | "focus">}`; // template literalAssertions#
const input = document.querySelector<HTMLInputElement>("#email"); // generic
const box = document.getElementById("box") as HTMLDivElement | null; // as (unchecked)
const theme = { primary: "#2f5fe0" } satisfies Record<string, string>; // checked, keeps literal info
const value = new Map([["a", 1]]).get("a")!; // non-null (unchecked)Prefer narrowing and satisfies over as and !.
tsconfig essentials#
{
"compilerOptions": {
"target": "es2022",
"module": "nodenext",
"strict": true,
"noUncheckedIndexedAccess": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true,
"outDir": "dist"
},
"include": ["src"]
}npx tsc compiles, npx tsc --noEmit only type-checks, npx tsc --watch recompiles on save.
Want the full explanations? Go to the Learn TypeScript course →