TypeScript Cheatsheet

Functions

Use this TypeScript reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Function Declarations and Expressions

// Named function declaration
function add(a: number, b: number): number {
  return a + b;
}

// Function expression
const multiply = function(a: number, b: number): number {
  return a * b;
};

// Arrow function
const divide = (a: number, b: number): number => a / b;

// Arrow function with body
const greet = (name: string): string => {
  return `Hello, ${name}`;
};

// Inferred return type (TypeScript infers from the return statement)
const square = (n: number) => n * n; // inferred: (n: number) => number

Parameter Types

// Required parameters
function greet(name: string, age: number): string {
  return `${name} is ${age}`;
}

// Optional parameters (must come after required)
function log(message: string, level?: string): void {
  console.log(`[${level ?? "info"}] ${message}`);
}

// Default parameters (implicitly optional)
function repeat(s: string, times: number = 3): string {
  return s.repeat(times);
}

// Rest parameters — collects remaining args as array
function sum(first: number, ...rest: number[]): number {
  return rest.reduce((acc, n) => acc + n, first);
}

// Destructured parameter with type
function display({ title, count = 0 }: { title: string; count?: number }): void {
  console.log(title, count);
}

// Named destructure with explicit type alias
type Options = { verbose: boolean; timeout: number };
function run({ verbose, timeout }: Options): void {}

Return Types

// Explicit return type
function parse(s: string): number { return parseInt(s); }

// void — returns undefined
function noop(): void {}

// never — function never returns (throws or infinite loops)
function fail(msg: string): never { throw new Error(msg); }

// Async function — always returns Promise<T>
async function fetchUser(id: number): Promise<User> {
  return await fetch(`/users/${id}`).then(r => r.json());
}

// Generator — returns Generator<YieldType, ReturnType, NextType>
function* range(start: number, end: number): Generator<number> {
  for (let i = start; i < end; i++) yield i;
}

// Async generator
async function* streamLines(url: string): AsyncGenerator<string> {
  // yields lines one by one
}

Function Overloads

// Declare overload signatures first, then implementation
function makeDate(timestamp: number): Date;
function makeDate(m: number, d: number, y: number): Date;
// Implementation signature (not callable directly)
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
  if (d !== undefined && y !== undefined) {
    return new Date(y, mOrTimestamp, d);
  }
  return new Date(mOrTimestamp);
}

const d1 = makeDate(12345678);
const d2 = makeDate(5, 5, 2000);

// Overloads with different return types
function process(x: string): string;
function process(x: number): number;
function process(x: string | number): string | number {
  return typeof x === "string" ? x.toUpperCase() : x * 2;
}

Function Types

// Type alias for a function
type BinaryOp = (a: number, b: number) => number;

const add: BinaryOp = (a, b) => a + b;

// Interface with call signature
interface Formatter {
  (input: string, radix?: number): string;
}

// Callback type
type Callback<T> = (err: Error | null, result: T) => void;

// Generic function type
type Mapper<A, B> = (value: A, index: number) => B;

// Variadic function type
type Logger = (...args: unknown[]) => void;

Generic Functions

// Single type parameter
function identity<T>(value: T): T {
  return value;
}
identity<string>("hello");
identity(42); // inferred: number

// Multiple type parameters
function pair<A, B>(a: A, b: B): [A, B] {
  return [a, b];
}

// Constrained type parameter
function getLength<T extends { length: number }>(arg: T): number {
  return arg.length;
}
getLength("hello");  // OK
getLength([1, 2, 3]); // OK

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

// Default type parameter
function createArray<T = string>(len: number, fill: T): T[] {
  return Array(len).fill(fill);
}

this Parameter

// Explicit this parameter (fake param, removed at compile time)
interface Deck {
  suits: string[];
  cards: number[];
  createCardPicker(this: Deck): () => { suit: string; card: number };
}

// this: void — function must not use 'this'
function noThis(this: void, x: number): number {
  return x;
}

// noImplicitThis — requires explicit this type in methods
const obj = {
  value: 42,
  getValue(this: typeof obj): number {
    return this.value;
  }
};

Higher-Order Functions

// Function that returns a function
function multiplier(factor: number): (n: number) => number {
  return (n) => n * factor;
}
const triple = multiplier(3);
triple(5); // 15

// Function that accepts a function
function applyTwice<T>(fn: (x: T) => T, value: T): T {
  return fn(fn(value));
}
applyTwice((x: number) => x + 1, 5); // 7

// Compose
function compose<A, B, C>(f: (b: B) => C, g: (a: A) => B): (a: A) => C {
  return (a) => f(g(a));
}

// Curry (manual)
function curry<A, B, C>(fn: (a: A, b: B) => C): (a: A) => (b: B) => C {
  return (a) => (b) => fn(a, b);
}

Optional and Default Parameters in Depth

// Optional parameters become T | undefined inside the function
function find(arr: string[], pred?: (s: string) => boolean): string | undefined {
  if (!pred) return arr[0];
  return arr.find(pred);
}

// Default parameters allow passing undefined explicitly
function greet(name: string = "World"): string {
  return `Hello, ${name}!`;
}
greet();            // "Hello, World!"
greet(undefined);   // "Hello, World!"
greet("Alice");     // "Hello, Alice!"

// Default can be an expression evaluated at call time
function now(date: Date = new Date()): Date {
  return date;
}

Async Functions and Promises

// Basic async/await
async function fetchData(url: string): Promise<string> {
  const res = await fetch(url);
  return res.text();
}

// Error handling
async function safeLoad(url: string): Promise<string | null> {
  try {
    const res = await fetch(url);
    if (!res.ok) return null;
    return await res.text();
  } catch {
    return null;
  }
}

// Promise.all — parallel, all must succeed
async function loadAll(urls: string[]): Promise<string[]> {
  return Promise.all(urls.map(url => fetchData(url)));
}

// Promise.allSettled — parallel, handles failures
async function loadSafe(urls: string[]) {
  const results = await Promise.allSettled(urls.map(fetchData));
  return results.map(r => r.status === "fulfilled" ? r.value : null);
}

// Promise.race — first to resolve/reject wins
async function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
  const timeout = new Promise<T>((_, reject) =>
    setTimeout(() => reject(new Error("Timeout")), ms)
  );
  return Promise.race([promise, timeout]);
}

Type Predicates and Assertion Functions

// Type predicate — return type is "val is T"
function isString(val: unknown): val is string {
  return typeof val === "string";
}

function isError(val: unknown): val is Error {
  return val instanceof Error;
}

// Usage
const values: unknown[] = [1, "two", true];
const strings = values.filter(isString); // string[]

// Assertion function — asserts a condition
function assert(condition: boolean, msg: string): asserts condition {
  if (!condition) throw new Error(msg);
}

function assertDefined<T>(val: T | undefined): asserts val is T {
  if (val === undefined) throw new Error("Expected defined value");
}

// After calling assertDefined, TypeScript knows val is T
function process(val: string | undefined) {
  assertDefined(val);
  val.toUpperCase(); // OK — val is string here
}

Function Signatures in Interfaces and Types

// Inline function type
let fn: (a: string) => number;

// Named function type
type Predicate<T> = (val: T) => boolean;
type Transform<A, B> = (val: A) => B;
type Effect<T> = (val: T) => void;
type AsyncOp<T, R> = (val: T) => Promise<R>;

// Method vs property function (subtly different for `this`)
interface Api {
  get(url: string): Promise<unknown>;      // method: covariant (bivariant in practice)
  post: (url: string) => Promise<unknown>; // property: stricter
}

// Overloaded interface method
interface Printer {
  print(doc: string): void;
  print(doc: string, copies: number): void;
}

Contextual Typing and Callbacks

// TypeScript infers callback parameter types from context
const nums = [1, 2, 3];
nums.forEach(n => n.toFixed()); // n inferred as number

// addEventListener infers event type
document.addEventListener("click", (e) => {
  e.clientX; // MouseEvent — inferred
});

// Map callback — inferred
const doubled = nums.map(n => n * 2); // n: number

// Generic callback with explicit typing
function transform<T, U>(arr: T[], fn: (item: T, i: number) => U): U[] {
  return arr.map(fn);
}
transform([1, 2, 3], (n, i) => `${i}:${n}`); // string[]

Parameter Spread and Tuple Rest

// Spread tuple as arguments
function point(x: number, y: number, z: number): void {}
const coords: [number, number, number] = [1, 2, 3];
point(...coords); // OK

// Generic rest parameters with tuple
function bind<T extends unknown[], R>(
  fn: (...args: T) => R,
  ...args: T
): () => R {
  return () => fn(...args);
}

// Variadic tuple types
type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U];
type AB = Concat<[string, number], [boolean]>; // [string, number, boolean]