← Home

TypeScript Types Cheatsheet

Complete TypeScript type system reference — from basic primitives and object shapes to generics, utility types, conditional types, and advanced patterns. Your go-to resource for every TypeScript type.

Basic Types

let s: string = "hello";
Text values — single or double quotes, template literals
let n: number = 42;
All numbers — integers, floats, hex (0xff), binary (0b1010), octal (0o744)
let b: boolean = true;
Logical values — true or false only
let x: null = null;
Intentional absence of a value
let y: undefined = undefined;
Variable declared but not yet assigned
function log(msg: string): void {}
Function returns nothing — no return value or returns undefined
function fail(msg: string): never { throw new Error(msg); }
Function never returns — always throws or infinite loops
let a: any = "anything";
Opts out of type checking — avoid when possible
let u: unknown = getData();
Type-safe counterpart of any — must narrow before use
let big: bigint = 100n;
Arbitrary-precision integers (ES2020+)
let sym: symbol = Symbol("id");
Unique and immutable primitive — used as object keys

Object Types

interface User {
  name: string;
  age: number;
}
Named shape for objects — can be extended and merged
type User = {
  name: string;
  age: number;
};
Type alias for object shapes — cannot be merged, but supports unions
interface User {
  email?: string;
}
Optional property — may be undefined, doesn't need to be provided
interface User {
  readonly id: number;
}
Readonly property — cannot be reassigned after creation
interface Dict {
  [key: string]: number;
}
Index signature — any string key maps to a number value
type StringMap = Record<string, string>;
Record utility — construct a type with keys K and values V
type Staff = User & { role: string };
Intersection — combine multiple types into one (all properties required)
interface Admin extends User {
  permissions: string[];
}
Extend an interface — inherits all properties and adds new ones
interface Config extends BaseConfig, LogConfig {}
Extend multiple interfaces at once
type Nested = {
  address: {
    street: string;
    city: string;
  };
};
Nested object types — inline anonymous types

Array & Tuple

let nums: number[] = [1, 2, 3];
Array of numbers — shorthand syntax
let strs: Array<string> = ["a", "b"];
Array of strings — generic syntax (equivalent to string[])
let ro: readonly number[] = [1, 2, 3];
Readonly array — push, pop, and index assignment are compile errors
let ro2: ReadonlyArray<number> = [1, 2];
Readonly array — generic syntax
let pair: [string, number] = ["age", 30];
Tuple — fixed-length array with known types at each position
let triple: [string, number, boolean];
Tuple with three elements — each position has a specific type
type NamedTuple = [first: string, last: string];
Named tuple elements — improves readability and IDE hints
let opt: [string, number?] = ["only"];
Optional tuple elements — trailing elements can be omitted
let rest: [string, ...number[]] = ["id", 1, 2, 3];
Rest elements in tuples — variable length after fixed positions
const t = ["a", 1] as const;
Const assertion on tuple — inferred as readonly ["a", 1], not (string | number)[]

Union & Literal Types

let id: string | number;
Union type — value can be one of several types
type Direction = "north" | "south" | "east" | "west";
String literal union — restricts to exact string values
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
Numeric literal union — restricts to specific numbers
type Result =
  | { status: "ok"; data: string }
  | { status: "err"; error: Error };
Discriminated union — shared literal property enables exhaustive narrowing
if (typeof x === "string") { /* x is string */ }
Type narrowing with typeof — compiler narrows in the branch
if (x instanceof Date) { /* x is Date */ }
Type narrowing with instanceof — works for class instances
if ("name" in obj) { /* obj has name */ }
Type narrowing with in — checks for property existence
function isString(x: unknown): x is string {
  return typeof x === "string";
}
Type guard — custom function that narrows types via type predicate
function assert(x: unknown): asserts x is string {
  if (typeof x !== "string") throw new Error();
}
Assertion function — narrows type or throws
type NullableString = string | null | undefined;
Nullable type — explicitly allow null or undefined

Generics

function identity<T>(arg: T): T {
  return arg;
}
Basic generic function — T is inferred from the argument
function first<T>(arr: T[]): T | undefined {
  return arr[0];
}
Generic with array — works for any element type
function getLength<T extends { length: number }>(x: T): number {
  return x.length;
}
Constrained generic — T must have a length property
function merge<T, U>(a: T, b: U): T & U {
  return { ...a, ...b };
}
Multiple generic parameters — combine two types
function create<T = string>(): T[] {
  return [];
}
Default generic type — falls back when not specified
interface Box<T> {
  value: T;
}
Generic interface — parameterize the shape of an object
type ApiResponse<T> = {
  data: T;
  error: string | null;
};
Generic type alias — reusable wrapper types
class Stack<T> {
  private items: T[] = [];
  push(item: T) { this.items.push(item); }
  pop(): T | undefined { return this.items.pop(); }
}
Generic class — type-safe data structures
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}
keyof constraint — K must be a key of T, return type is T[K]
type Pair<T> = T extends unknown[] ? T[number] : T;
Conditional generic — different result based on type parameter

Utility Types

Partial<User>
Makes all properties optional — { name?: string; age?: number }
Required<User>
Makes all properties required — removes optional modifiers
Readonly<User>
Makes all properties readonly — prevents reassignment
Pick<User, "name" | "email">
Select a subset of properties from a type
Omit<User, "password">
Remove specific properties from a type
Record<string, number>
Construct a type with keys of one type and values of another
Exclude<"a" | "b" | "c", "a">
Remove members from a union — result: "b" | "c"
Extract<"a" | "b" | "c", "a" | "f">
Keep only shared members — result: "a"
NonNullable<string | null | undefined>
Remove null and undefined from a type — result: string
ReturnType<typeof myFunc>
Extract the return type of a function type
Parameters<typeof myFunc>
Extract parameter types as a tuple — [arg1: string, arg2: number]
ConstructorParameters<typeof MyClass>
Extract constructor parameter types as a tuple
InstanceType<typeof MyClass>
Extract the instance type of a class constructor
Awaited<Promise<string>>
Unwrap a Promise type recursively — result: string
ThisParameterType<typeof fn>
Extract the this parameter type from a function
OmitThisParameter<typeof fn>
Remove the this parameter from a function type

Advanced Types

type IsString<T> = T extends string ? "yes" : "no";
Conditional type — evaluates based on a type relationship
type Flatten<T> = T extends (infer U)[] ? U : T;
infer keyword — extract a type within a conditional type
type Optional<T> = {
  [K in keyof T]?: T[K];
};
Mapped type — transform each property of an existing type
type ReadonlyMap<T> = {
  readonly [K in keyof T]: T[K];
};
Mapped type with modifier — add readonly to every property
type RemoveReadonly<T> = {
  -readonly [K in keyof T]: T[K];
};
Remove modifier in mapped type — strip readonly from all properties
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
Key remapping in mapped types — transform property names
type EventName = `on${"Click" | "Hover" | "Focus"}`;
Template literal type — result: "onClick" | "onHover" | "onFocus"
type PropType = User["name"];
Indexed access type — extract the type of a specific property
type Keys = keyof User;
keyof operator — union of all property names: "name" | "age" | "email"
const user = { name: "Alex", age: 30 };
type UserType = typeof user;
typeof operator — extract type from a value at compile time
const cfg = { api: "/v1" } satisfies Record<string, string>;
satisfies — validate a type without widening the inferred type
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object
    ? DeepReadonly<T[K]>
    : T[K];
};
Recursive conditional mapped type — apply transformations deeply

Common Patterns

const el = document.getElementById("app") as HTMLDivElement;
Type assertion — tell the compiler you know the type better
const el = <HTMLDivElement>document.getElementById("app");
Angle-bracket assertion — same as 'as' but can't use in JSX files
const name = user!.name;
Non-null assertion — tells compiler the value is not null/undefined
const routes = ["home", "about", "contact"] as const;
Const assertion — inferred as readonly tuple of literal types
type Route = (typeof routes)[number];
Extract union from const array — result: "home" | "about" | "contact"
enum Status {
  Active = "ACTIVE",
  Inactive = "INACTIVE",
}
String enum — named constants with string values
type Status = "active" | "inactive";
Union alternative to enum — simpler, no runtime code, tree-shakable
type Brand<T, B> = T & { __brand: B };
type USD = Brand<number, "USD">;
Branded types — prevent mixing semantically different values of same base type
type Prettify<T> = {
  [K in keyof T]: T[K];
} & {};
Prettify helper — forces TypeScript to expand intersected types for readability
declare module "*.css" {
  const styles: Record<string, string>;
  export default styles;
}
Module augmentation — declare types for non-TS imports
function process(input: string): string;
function process(input: number): number;
function process(input: string | number) {
  return input;
}
Function overloads — different return types based on input types
type Exact<T, Shape> = T extends Shape
  ? Exclude<keyof T, keyof Shape> extends never
    ? T
    : never
  : never;
Exact type — reject objects with extra properties at type level

FAQ

What is the difference between interface and type in TypeScript?

Both can describe object shapes, but they have key differences. Interfaces support declaration merging (defining the same interface twice combines them) and can be extended with 'extends'. Type aliases support unions, intersections, mapped types, conditional types, and primitives. Use interfaces for public API contracts and object shapes that might be extended. Use type aliases for unions, complex type transformations, and when you need features interfaces don't support. In practice, both work for most cases — pick one convention and be consistent.

When should I use any vs unknown?

Prefer unknown over any in almost all cases. any disables type checking entirely — you can do anything with an any value without errors, which defeats the purpose of TypeScript. unknown is the type-safe alternative: you must narrow the type (with typeof, instanceof, or a type guard) before you can use the value. Use unknown for values from external sources (API responses, user input, JSON.parse), and reserve any for rapid prototyping or complex third-party library integration where types aren't available.

When should I use generics?

Use generics when you need a function, class, or type that works with multiple types while preserving type relationships. Common scenarios: container types (Box<T>, ApiResponse<T>), utility functions that pass through types (identity, pick, merge), collection operations (first element of an array), and when you find yourself writing the same logic for different types. Don't use generics when a simple union type suffices or when the type parameter is never used in a meaningful constraint.

What are utility types and when should I use them?

Utility types are built-in generic types that transform other types. The most common: Partial<T> for optional fields (great for update functions), Pick<T, K>/Omit<T, K> for selecting subsets of properties, Record<K, V> for dictionaries, ReturnType<T> for extracting function return types, and Readonly<T> for immutable data. They eliminate repetitive type definitions and keep your types DRY. Combine them for powerful patterns: Partial<Pick<User, 'name' | 'email'>> makes only selected fields optional.

Related Resources