M
MeshWorld.
Cheatsheet TypeScript JavaScript Web Dev Frontend Developer Tools Types 8 min read

TypeScript Cheat Sheet: Types, Generics & Utility Types

By Vishnu Damwala

Quick reference tables

Primitive types

TypeExampleNotes
string"hello"Text
number42, 3.14Integers and floats
booleantrue, false
nullnullExplicit absence
undefinedundefinedUninitialized
bigint9007199254740991nLarge integers
symbolSymbol("id")Unique identifiers
anyanythingOpt out of type checking
unknownanythingSafe version of any (must narrow before use)
neverunreachable codeFunction that always throws / infinite loop
voidundefinedReturn type of functions that don’t return

Type declaration syntax

SyntaxWhat it does
let x: string = "hi"Explicit annotation
let x = "hi"Inferred (preferred)
const user: User = { ... }Object typed to interface
function fn(x: string): numberParameter + return type
const fn = (x: string): number => ...Arrow function
type ID = string | numberType alias
interface User { name: string }Interface
type Point = { x: number; y: number }Object type alias

Union & intersection

SyntaxMeaning
string | numberEither string OR number
A & BBoth A AND B (merged)
"left" | "right" | "center"String literal union
1 | 2 | 3Numeric literal union
true | false(same as boolean)

Utility types

UtilityWhat it does
Partial<T>All properties optional
Required<T>All properties required
Readonly<T>All properties read-only
Record<K, V>Object with keys K and values V
Pick<T, K>Keep only keys K from T
Omit<T, K>Remove keys K from T
Exclude<T, U>Remove U from union T
Extract<T, U>Keep only U from union T
NonNullable<T>Remove null and undefined
ReturnType<T>Get return type of a function
Parameters<T>Get parameters as tuple
Awaited<T>Unwrap Promise type
InstanceType<T>Get instance type of a class

Type narrowing / guards

PatternWhat it does
typeof x === "string"Narrow to string
x instanceof DateNarrow to class instance
"prop" in objNarrow by property existence
x !== null && x !== undefinedNarrow away nulls
Custom type guard functionfunction isUser(x): x is User

Generics — common patterns

SyntaxMeaning
function fn<T>(arg: T): TGeneric function
<T extends string>Constrain T to string
<T extends object>Constrain T to object
<T, K extends keyof T>K must be a key of T
Array<T> or T[]Generic array
Promise<T>Generic promise
Map<K, V>Generic map

tsconfig — key options

OptionWhat it does
"strict": trueEnable all strict checks (recommended)
"target": "ES2022"JS version to compile to
"module": "ESNext"Module system
"moduleResolution": "bundler"For Vite/Webpack projects
"outDir": "./dist"Compiled output directory
"rootDir": "./src"Source root
"paths"Path aliases (@/src/)
"baseUrl": "."Root for path resolution
"noEmit": trueType-check only, no output
"declaration": trueGenerate .d.ts files
"sourceMap": trueGenerate source maps
"allowJs": trueAllow .js files
"esModuleInterop": trueFix CommonJS/ESM interop
"skipLibCheck": trueSkip type-checking node_modules

Detailed sections

Interfaces vs type aliases

// Interface — extendable, good for objects and classes
interface User {
  id: number;
  name: string;
  email?: string; // optional
}

// Extend an interface
interface AdminUser extends User {
  role: "admin" | "superadmin";
}

// Type alias — more flexible, good for unions and computed types
type Status = "active" | "inactive" | "pending";
type ID = string | number;
type UserOrAdmin = User | AdminUser;

// When to use which:
// - Interface: when defining object shapes, especially for classes
// - Type alias: for unions, primitives, computed types, or when you need `Exclude`/`Extract`

Generics in practice

// Generic function
function first<T>(arr: T[]): T | undefined {
  return arr[0];
}

const n = first([1, 2, 3]);     // type: number | undefined
const s = first(["a", "b"]);    // type: string | undefined

// Generic with constraint
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { name: "Alice", age: 30 };
getProperty(user, "name"); // string
getProperty(user, "age");  // number
// getProperty(user, "email"); // Error: not a key of user

// Generic interface
interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

type UserResponse = ApiResponse<User>;
type ListResponse = ApiResponse<User[]>;

Utility types — real examples

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
  role: "user" | "admin";
}

// Partial — all optional (useful for update functions)
function updateUser(id: number, data: Partial<User>) { ... }
updateUser(1, { name: "Alice" }); // only name, no other fields required

// Pick — keep only what you need (useful for API responses)
type PublicUser = Pick<User, "id" | "name" | "role">;
// { id: number; name: string; role: "user" | "admin" }

// Omit — remove sensitive fields
type SafeUser = Omit<User, "password">;

// Record — typed dictionary/map
type RolePermissions = Record<User["role"], string[]>;
const perms: RolePermissions = {
  user: ["read"],
  admin: ["read", "write", "delete"],
};

// ReturnType — get what a function returns
async function fetchUser(id: number) {
  return { id, name: "Alice", role: "user" as const };
}
type FetchedUser = Awaited<ReturnType<typeof fetchUser>>;
// { id: number; name: string; role: "user" }

Type guards

// typeof guard
function format(value: string | number): string {
  if (typeof value === "string") {
    return value.toUpperCase(); // TypeScript knows it's string here
  }
  return value.toFixed(2); // TypeScript knows it's number here
}

// instanceof guard
function processDate(value: Date | string): Date {
  if (value instanceof Date) {
    return value;
  }
  return new Date(value);
}

// Custom type guard (predicate function)
interface Cat { meow(): void }
interface Dog { bark(): void }

function isCat(pet: Cat | Dog): pet is Cat {
  return (pet as Cat).meow !== undefined;
}

function makeSound(pet: Cat | Dog) {
  if (isCat(pet)) {
    pet.meow(); // TypeScript knows it's Cat
  } else {
    pet.bark(); // TypeScript knows it's Dog
  }
}

// Discriminated union — the cleanest pattern
type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "rectangle"; width: number; height: number }
  | { kind: "triangle"; base: number; height: number };

function area(shape: Shape): number {
  switch (shape.kind) {
    case "circle": return Math.PI * shape.radius ** 2;
    case "rectangle": return shape.width * shape.height;
    case "triangle": return 0.5 * shape.base * shape.height;
  }
}

as const — immutable literal types

// Without as const — type is string[]
const directions = ["north", "south", "east", "west"];
// type: string[]

// With as const — type is readonly tuple of literals
const directions = ["north", "south", "east", "west"] as const;
// type: readonly ["north", "south", "east", "west"]

type Direction = typeof directions[number];
// type: "north" | "south" | "east" | "west"

// Great for config objects
const config = {
  env: "production",
  version: 3,
  features: ["auth", "analytics"],
} as const;

type Env = typeof config.env; // "production" (not string)

Mapped types

// Make all properties nullable
type Nullable<T> = { [K in keyof T]: T[K] | null };

// Make all properties async getters
type AsyncGetters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => Promise<T[K]>;
};

// Filter properties by value type
type OnlyStrings<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K];
};

interface Mixed { name: string; age: number; email: string; active: boolean }
type StringFields = OnlyStrings<Mixed>;
// { name: string; email: string }

Template literal types

type EventName = "click" | "focus" | "blur";
type HandlerName = `on${Capitalize<EventName>}`;
// "onClick" | "onFocus" | "onBlur"

type CSSProperty = "margin" | "padding";
type CSSDirections = "top" | "right" | "bottom" | "left";
type CSSLonghand = `${CSSProperty}-${CSSDirections}`;
// "margin-top" | "margin-right" | ... | "padding-left"

// Useful for typed event emitters
type EventMap = {
  "user:login": { userId: string };
  "user:logout": { userId: string };
  "order:created": { orderId: number };
};

type EventKey = keyof EventMap; // "user:login" | "user:logout" | "order:created"

Strict tsconfig for new projects

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "exactOptionalPropertyTypes": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "declaration": true,
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}