TypeScript Cheatsheet
Interfaces and Type Aliases
Use this TypeScript reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Interface Declaration
interface User { id: number; name: string; email: string; } // Optional property interface Config { host: string; port?: number; // number | undefined } // Readonly property interface Point { readonly x: number; readonly y: number; } // Method shorthand vs property function syntax interface Greeter { greet(name: string): string; // method shorthand greetFn: (name: string) => string; // property function }
Type Alias Declaration
type UserId = string; type Point = { x: number; y: number }; type Callback = (err: Error | null, data: string) => void; // Union and intersection only possible with type, not interface type StringOrNumber = string | number; type Named = { name: string }; type Aged = { age: number }; type Person = Named & Aged; // Recursive type alias type Json = | string | number | boolean | null | Json[] | { [key: string]: Json }; // Conditional type alias type IsString<T> = T extends string ? true : false;
Interface vs Type Alias: Key Differences
| Feature | interface | type |
|---|---|---|
| Declaration merging | Yes | No |
| Extends another interface | Yes (extends) | Via intersection (&) |
| Implements in class | Yes | Yes (if object shape) |
| Union types | No | Yes |
| Intersection types | No (use extends) | Yes (&) |
| Conditional types | No | Yes |
| Mapped types | No | Yes |
| Computed property keys | Limited | Yes (via mapped) |
| Tuple types | No | Yes |
| Primitive aliases | No | Yes (type Id = string) |
Prefer
interfacefor public API shapes (object types, classes). Prefertypefor unions, tuples, mapped types, and computed types.
Extending Interfaces
interface Animal { name: string; } interface Dog extends Animal { breed: string; } // Multiple inheritance interface FlyingDog extends Dog, Animal { wingspan: number; } // Extending with modifications (can't narrow, but can widen) interface Pet extends Animal { owner: string; } const d: Dog = { name: "Rex", breed: "Lab" };
Declaration Merging (interface only)
// Multiple declarations of the same interface are merged interface Box { width: number; } interface Box { height: number; } // Box = { width: number; height: number } // Useful for augmenting third-party types interface Window { myCustomProp: string; } // Module augmentation declare module "express" { interface Request { user?: { id: string }; } }
Index Signatures
// String index interface StringMap { [key: string]: string; } const m: StringMap = { foo: "bar", baz: "qux" }; // Number index interface NumberMap { [index: number]: string; } const arr: NumberMap = ["a", "b", "c"]; // Known properties + index signature (all props must match index type) interface Mixed { length: number; [key: string]: number; // length must also be number — it is, so OK } // Readonly index signature interface ReadonlyStringMap { readonly [key: string]: string; }
Call Signatures and Construct Signatures
// Call signature — makes an object callable interface Formatter { (value: string): string; prefix: string; // also has a property } // Construct signature — makes an object newable interface Constructor<T> { new (arg: string): T; } // Both interface ClockConstructor { new (hour: number, minute: number): ClockInterface; } interface ClockInterface { tick(): void; } // Function with properties interface FnWithProps { (x: number): number; defaultValue: number; reset(): void; }
Intersection Types with type
type Named = { name: string }; type Aged = { age: number }; // Intersection combines all properties type Person = Named & Aged; const p: Person = { name: "Alice", age: 30 }; // Merging with override (same key, different type = never) type A = { x: string }; type B = { x: number }; type AB = A & B; // { x: never } — string & number = never // Correct way to override: use Omit type Override = Omit<A, "x"> & { x: number }; // Functional intersection type LogAndRun = ((x: number) => string) & { debug: boolean };
Generics in Interfaces and Types
// Generic interface interface Response<T> { data: T; status: number; message: string; } const res: Response<User[]> = { data: [], status: 200, message: "OK" }; // Generic type alias type Nullable<T> = T | null; type Pair<A, B> = { first: A; second: B }; type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E }; // Generic constraint interface Lengthwise { length: number; } interface Collection<T extends Lengthwise> { items: T[]; longest(): T; } // Default type parameter interface Container<T = string> { value: T; } const c: Container = { value: "hello" }; // T defaults to string
Readonly and Partial Modifiers
// Readonly in interface interface ImmutablePoint { readonly x: number; readonly y: number; } // Readonly<T> utility makes all props readonly type FrozenUser = Readonly<User>; // Partial<T> makes all props optional type PartialUser = Partial<User>; // Required<T> makes all props required type FullConfig = Required<Config>; // Deep readonly (manual) type DeepReadonly<T> = { readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]; };
Discriminated Unions with Type Aliases
// Each member has a literal discriminant field type Shape = | { kind: "circle"; radius: number } | { kind: "square"; side: number } | { kind: "rectangle"; width: number; height: number }; function area(shape: Shape): number { switch (shape.kind) { case "circle": return Math.PI * shape.radius ** 2; case "square": return shape.side ** 2; case "rectangle": return shape.width * shape.height; } } // Result / Either pattern type Result<T, E = string> = | { success: true; value: T } | { success: false; error: E }; function parse(s: string): Result<number> { const n = Number(s); if (isNaN(n)) return { success: false, error: "Not a number" }; return { success: true, value: n }; }
Recursive Interface / Type
// Recursive interface interface TreeNode { value: number; left?: TreeNode; right?: TreeNode; } // Recursive type alias type NestedList<T> = T | NestedList<T>[]; // Linked list interface ListNode<T> { data: T; next: ListNode<T> | null; } // Deeply nested object interface Category { name: string; children?: Category[]; }
Extracting and Transforming Types
interface User { id: number; name: string; email: string; password: string; } // Pick specific properties type PublicUser = Pick<User, "id" | "name" | "email">; // Omit specific properties type SafeUser = Omit<User, "password">; // Extract from union type OnlyStrings = Extract<string | number | boolean, string>; // string // Exclude from union type NoStrings = Exclude<string | number | boolean, string>; // number | boolean // NonNullable — remove null and undefined type Defined = NonNullable<string | null | undefined>; // string // ReturnType type FnReturn = ReturnType<() => User>; // User // Parameters type FnParams = Parameters<(a: string, b: number) => void>; // [string, number]
Interface Extending a Class
class Control { private state: any; } // Interface can extend a class (captures its shape including private/protected) interface SelectableControl extends Control { select(): void; } // Only subclasses of Control can implement SelectableControl class Button extends Control implements SelectableControl { select() {} }
Hybrid Types
// An object that acts as both a function and has properties interface Counter { (start: number): string; interval: number; reset(): void; } function getCounter(): Counter { const counter = function(start: number) { return String(start); } as Counter; counter.interval = 123; counter.reset = function() {}; return counter; } const c = getCounter(); c(10); c.interval = 5; c.reset();