Jump to section
TypeScript Cheatsheet
A practical guide to TypeScript types, generics, and modules you can use right away.
Basics: Types
TypeScript lets you say what kind of value a variable holds. Do that, and it'll warn you the moment something doesn't add up — before your code even runs.
the everyday types
TypeScript
// type goes after the variable name
let username: string = "Alice";
let age: number = 30;
let isActive: boolean = true;
let score: number = 9.5;null and undefined
TypeScript
// use | to allow null or undefined
let middleName: string | null = null;
// has no middle name
let nickname: string | undefined;
// not set yetany and unknown
TypeScript
let wild: any = "hello";
wild = 42;
// ✅ allowed — but no safety net
let safe: unknown = "hello";
// can't use directly ❌
// safe.toUpperCase();
// check type first ✅
if (typeof safe === "string") {
safe.toUpperCase();
}Prefer `unknown` over `any`. With `unknown`, TypeScript forces you to check the type first — keeping you safe.
literal types
TypeScript
// only these four words are allowed
let direction: "left" | "right" | "up" | "down";
direction = "left";
// ✅ allowed
direction = "diagonal";
// ❌ not in the list
// only dice values 1–6
let roll: 1 | 2 | 3 | 4 | 5 | 6;Arrays & Tuples
Arrays hold a list of values all the same type. Tuples are stricter — a fixed list where each position has its own specific type.
typed arrays
TypeScript
let scores: number[] = [10, 20, 30];
let names: string[] = ["Alice", "Bob"];
let flags: boolean[] = [true, false];
// same thing, different syntax
let scores2: Array<number> = [10, 20, 30];tuples — fixed positions
TypeScript
// position 0 = string, position 1 = number
let person: [string, number] = ["Alice", 30];
let name = person[0];
// "Alice" — TypeScript knows it's a string
let age = person[1];
// 30 — TypeScript knows it's a number
// real-world: [x, y] coordinates
let point: [number, number] = [10, 20];Use tuples when order and meaning of each position matters — like `[x, y]` coordinates or `[status, message]` pairs.
Functions
You can type both what goes in and what comes out of a function. TypeScript then catches it immediately if you pass the wrong thing or forget to return something.
typed function basics
TypeScript
// name must be string, returns string
function greet(name: string): string {
return "Hello, " + name;
}
// a and b must be numbers, returns number
function add(a: number, b: number): number {
return a + b;
}
// arrow function — same rules
const multiply = (
a: number,
b: number
): number => a * b;optional and default parameters
TypeScript
// optional with ?
function greet(
name: string,
greeting?: string
): string {
return (greeting ?? "Hello") + ", " + name;
}
greet("Alice");
// "Hello, Alice"
greet("Alice", "Hi");
// "Hi, Alice"
// default value
function greetDefault(
name: string,
greeting: string = "Hello"
): string {
return greeting + ", " + name;
}void — functions that return nothing
TypeScript
function logMessage(msg: string): void {
console.log(msg);
// just prints — returns nothing
}rest parameters
TypeScript
function sum(...numbers: number[]): number {
return numbers.reduce((total, n) => total + n, 0);
}
sum(1, 2, 3);
// 6
sum(10, 20, 30, 40);
// 100Interfaces & Type Aliases
Instead of repeating the same object shape everywhere, give it a name. Then use that name as a type anywhere you need it.
interface — describe an object's shape
TypeScript
interface User {
id: number;
name: string;
email?: string;
// optional — can be missing
}
const user1: User = {
id: 1, name: "Alice"
};
// ✅ no email needed
const user2: User = {
id: 2, name: "Bob", email: "[email protected]"
};
// ✅ with emailtype alias — name any type
TypeScript
// name a union type
type ID = string | number;
let userId: ID = "abc123"; // ✅
let postId: ID = 42; // ✅
// name an object shape
type Point = { x: number; y: number };
const origin: Point = { x: 0, y: 0 };Use `interface` for object shapes you may extend later. Use `type` for unions, primitives, and computed types.
extending — build on top of another type
TypeScript
interface Animal {
name: string;
}
// Dog gets everything Animal has, plus 'breed'
interface Dog extends Animal {
breed: string;
}
const dog: Dog = {
name: "Rex",
breed: "Labrador"
};
// ✅Classes
A class is a template for creating objects. TypeScript adds type-checking on top — so you can't accidentally use a class the wrong way.
basic class
TypeScript
class Animal {
name: string; // typed property
constructor(name: string) {
this.name = name;
}
speak(): string {
return this.name + " makes a sound.";
}
}
const cat = new Animal("Cat");
cat.speak();
// "Cat makes a sound."access modifiers
TypeScript
class BankAccount {
public owner: string;
private balance: number;
readonly id: string;
constructor(
owner: string,
start: number,
id: string
) {
this.owner = owner;
this.balance = start;
this.id = id;
}
deposit(amount: number): void {
this.balance += amount;
// ✅ inside the class — allowed
}
}
const acc = new BankAccount("Alice", 100, "ACC-001");
acc.owner;
// ✅ public
// acc.balance
// ❌ private — TypeScript errorinheritance
TypeScript
class Animal {
constructor(public name: string) {}
speak(): string {
return this.name + " makes a sound.";
}
}
class Dog extends Animal {
constructor(
name: string,
public breed: string
) {
super(name);
// run Animal's constructor first
}
// override parent method
speak(): string {
return this.name + " barks!";
}
}
const dog = new Dog("Rex", "Labrador");
dog.speak()
// "Rex barks!"
dog.breed
// "Labrador"Unions & Intersections
Unions let a value be one of several types. Intersections combine multiple types into one. Both are used constantly in real TypeScript code.
union — this OR that
TypeScript
// id can be a number or a string
let id: string | number;
id = 101; // ✅
id = "abc"; // ✅
id = true; // ❌ not in the union
function printId(id: string | number): void {
console.log("ID: " + id);
}discriminated union — safe branching
TypeScript
type Circle = {
kind: "circle";
radius: number
};
type Rectangle = {
kind: "rectangle";
width: number;
height: number
};
type Shape = Circle | Rectangle;
function getArea(shape: Shape): number {
if (shape.kind === "circle") {
// TypeScript knows it's Circle here
return Math.PI * shape.radius ** 2;
}
// TypeScript knows it's Rectangle here
return shape.width * shape.height;
}Use a discriminated union when you have different object shapes in the same variable — it gives full type safety inside each branch.
intersection — this AND that
TypeScript
type HasName = { name: string };
type HasEmail = { email: string };
// Contact must have BOTH name AND email
type Contact = HasName & HasEmail;
const contact: Contact = {
name: "Alice",
// required by HasName
email: "[email protected]",
// required by HasEmail
};Type Narrowing
When a value could be multiple types, TypeScript narrows it down inside if checks — so you always get the right methods and no false errors.
typeof — check the type at runtime
TypeScript
function printValue(value: string | number): void {
if (typeof value === "string") {
// TypeScript knows it's string here
console.log(value.toUpperCase());
} else {
// TypeScript knows it's number here
console.log(value.toFixed(2));
}
}instanceof — check for a class
TypeScript
function formatDate(value: string | Date): string {
if (value instanceof Date) {
// TypeScript knows it's Date here
return value.toISOString();
}
// TypeScript knows it's string here
return value;
}Generics
Generics let you write one function or interface that works with any type — while still keeping full type safety. Write it once, use it everywhere.
the problem generics solve
TypeScript
// ❌ without generics — repeat for every type
function wrapString(value: string): string[] {
return [value];
}
function wrapNumber(value: number): number[] {
return [value];
}
// ✅ with generics — one function for all types
function wrap<T>(value: T): T[] {
return [value];
}
wrap("hello");
// T = string → string[]
wrap(42);
// T = number → number[]
wrap(true);
// T = boolean → boolean[]constrained generics
TypeScript
// T must have a 'length' property
function longest<T extends { length: number }>(
a: T,
b: T
): T {
return a.length >= b.length ? a : b;
}
longest("alice", "bob");
// ✅ strings have .length
longest([1, 2, 3], [1, 2]);
// ✅ arrays have .length
// longest(1, 2);
// ❌ numbers have no .lengthgeneric with default type
TypeScript
// T defaults to string if not specified
interface Box<T = string> {
value: T;
label: string;
}
const strBox: Box = {
value: "hello",
label: "greeting"
};
// T = string (default used)
const numBox: Box<number> = {
value: 42,
label: "score"
};
// T = number (explicitly set)generic interfaces
TypeScript
interface ApiResponse<T> {
data: T;
success: boolean;
message: string;
}
// data is a User object
type UserResponse = ApiResponse<{
id: number;
name: string;
}>;
// data is a list of numbers
type ScoresResponse = ApiResponse<number[]>;Utility Types
TypeScript comes with ready-made helpers that transform your types. No imports needed — they just work out of the box.
Partial — make all fields optional
TypeScript
interface User {
id: number;
name: string;
email: string;
}
function updateUser(
id: number,
changes: Partial<User>
): void {
// changes can have any User fields
}
updateUser(1, { name: "Bob" });
// ✅ only updating name
updateUser(1, { email: "[email protected]" });
// ✅ only updating emailRequired — make all fields required
TypeScript
interface Config {
host?: string;
port?: number;
debug?: boolean;
}
// after setup, all fields must be present
function startServer(config: Required<Config>): void {
console.log(config.host);
// ✅ TypeScript knows this exists
}
startServer({ host: "localhost", port: 3000, debug: false });
// ✅
startServer({ host: "localhost" });
// ❌ port and debug are now requiredReadonly — prevent changes
TypeScript
interface Config {
apiUrl: string;
timeout: number;
}
const config: Readonly<Config> = {
apiUrl: "https://api.example.com",
timeout: 3000,
};
// config.apiUrl = "other";
// ❌ TypeScript error — read-onlyPick & Omit — select or remove fields
TypeScript
interface User {
id: number;
name: string;
email: string;
password: string;
}
// keep only id and name
type PublicProfile = Pick<User, "id" | "name">;
// { id: number; name: string }
// remove password
type SafeUser = Omit<User, "password">;
// { id: number; name: string; email: string }Record — build a key-value object type
TypeScript
// any string key, number value
const scores: Record<string, number> = {
Alice: 95,
Bob: 87,
};
// only specific keys allowed
type Role = "admin" | "editor" | "viewer";
type Permissions = Record<Role, boolean>;
const perms: Permissions = {
admin: true,
editor: true,
viewer: false,
};ReturnType — get a function's return type
TypeScript
function getUser() {
return { id: 1, name: "Alice", active: true };
}
// derive the type from the function — stays in sync
type User = ReturnType<typeof getUser>;
// { id: number; name: string; active: boolean }
// useful with API functions
async function fetchConfig() {
return { apiUrl: "https://...", timeout: 3000 };
}
type Config = Awaited<ReturnType<typeof fetchConfig>>;
// { apiUrl: string; timeout: number }Enums
Enums give friendly names to a fixed set of values. Your code becomes easier to read and harder to misuse — no more magic strings scattered everywhere.
string enums
TypeScript
enum Status {
Pending = "PENDING",
Active = "ACTIVE",
Inactive = "INACTIVE",
}
function activate(status: Status): void {
console.log("Status:", status);
}
activate(Status.Active);
// ✅ "Status: ACTIVE"
// activate("ACTIVE");
// ❌ must use the enumas const — modern alternative to enums
TypeScript
const STATUS = {
Pending: "PENDING",
Active: "ACTIVE",
Inactive: "INACTIVE",
} as const;
// derive type automatically
type Status =
typeof STATUS[keyof typeof STATUS];
// "PENDING" | "ACTIVE" | "INACTIVE"
function activate(status: Status): void {
console.log(status);
}
activate(STATUS.Active); // ✅
activate("ACTIVE"); // ✅ plain strings work tooMany teams prefer `as const` over `enum` — it works better with TypeScript's type tools and plain strings are accepted.
Modules — Import & Export
Split your code into separate files and share what's needed between them. TypeScript adds one extra trick: type-only imports that disappear completely at runtime.
exporting from a file
TypeScript
// math.ts
export const PI = 3.14159;
export function add(
a: number,
b: number
): number {
return a + b;
}
// export a type too
export type Point = { x: number; y: number };
// default export
export default function multiply(
a: number,
b: number
) {
return a * b;
}importing from another file
TypeScript
// default — no {}
import multiply from "./math";
// named — need {}
import { add, PI } from "./math";
// import everything as one object
import * as MathUtils from "./math";
MathUtils.add(1, 2) // 3
MathUtils.PI // 3.14159
// type-only — removed at runtime
import type { Point } from "./math";Always use `import type` when importing only types — it signals intent and has zero impact on the output bundle.
Advanced Types
These tools let you build new types from existing ones — so your types stay in sync with your code automatically. Most of these are intermediate to advanced.
keyof — get the keys of a type
TypeScript
interface User {
id: number;
name: string;
email: string;
}
type UserKeys = keyof User;
// "id" | "name" | "email"
// safe property lookup
function getField<T, K extends keyof T>(
obj: T,
key: K
): T[K] {
return obj[key];
}
const user = { id: 1, name: "Alice", email: "[email protected]" };
getField(user, "name");
// ✅ "Alice"
// getField(user, "role");
// ❌ "role" is not a key of Usertypeof — get the type of a value
TypeScript
const config = {
apiUrl: "https://api.example.com",
timeout: 3000,
debug: false,
};
// derive type from value — always in sync
type Config = typeof config;
// {
// apiUrl: string;
// timeout: number;
// debug: boolean;
// }mapped types — transform every field
TypeScript
// make every field in T a string
type StringVersion<T> = {
[Key in keyof T]: string;
};
interface User {
id: number;
name: string;
age: number;
}
type UserErrors = StringVersion<User>;
// {
// id: string;
// name: string;
// age: string;
// }
// useful for form error messagesconditional types — type-level if/else
TypeScript
// if T is string → true, otherwise → false
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// unwrap a Promise to get the value inside
type Unwrap<T> =
T extends Promise<infer Inner> ? Inner : T;
type Result = Unwrap<Promise<string>>;
// string
type Same = Unwrap<number>;
// number — not a Promise, returned as-istsconfig Essentials
The tsconfig.json file tells TypeScript how strict to be and what kind of JavaScript to output. And honestly, getting this right at the start saves a lot of pain later.
a good starting config
JSON
{
"compilerOptions": {
"target": "ES2022",
// what version of JS to output
"module": "ESNext",
// use modern import/export
"moduleResolution": "bundler",
// let your bundler handle imports
"lib": ["ES2022", "DOM"],
// available APIs
"strict": true,
// all safety checks ← most important
"outDir": "./dist",
// where compiled JS files go
"sourceMap": true
// makes debugging easier
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}what strict: true turns on
JSON
"strictNullChecks": true
// null/undefined aren't secretly everywhere
"noImplicitAny": true
// must write the type — no silent 'any'
"strictFunctionTypes": true
// function params checked more carefully
"alwaysStrict": true
// adds "use strict" to every output fileAlways start with `strict: true`. It's much easier than adding individual rules later to an existing codebase.
No login required to share feedback
More Cheatsheets
Keep your reference handy
Explore more zero-to-hero cheatsheets for the tools you use daily.