🗂️ Typescript Cheat Sheet

A collection of useful TypeScript utilities and best practices for safer, more expressive code.


Function Overload

In Next.js, you can call server functions from the client side directly. However, type hints may be misleading and cause errors if not handled carefully.

For example:

type FormData = {
  name: string
  age: string
}
export function serverAction(data: FormData) {
  const { name, age } = data
}

Problem: You cannot trust what the client submits—the name or age property may not exist in reality. Always validate incoming data on the server.

A safer approach:

function validate(data: unknown): FormData {
  // validate data here (e.g., using zod, yup, or manual checks)
  ...
  return data as FormData
}
export function serverAction(data: FormData) {
  const validData = validate(data)
  const { name, age } = data // Oops, should use validData here!
}

Pitfall: Declaring data as FormData is not ideal, since TypeScript will not warn you if you accidentally use data.name or data.age before validation. You should change its type to unknown:

export function serverAction(data: unknown) {
  const validData = validate(data)
  // const { name, age } = data // TypeScript error, as expected
  const { name, age } = validData
}

However, this approach loses type hints on the client side.
Solution: Use function overloads to provide type hints for the client, while keeping runtime safety on the server:

type FormData = {
  name: string
  age: string
}
// Overload for client-side type hints
export function serverAction(data: FormData): void
// Implementation for server-side safety
export function serverAction(data: unknown) {
  const validData = validate(data)
  const { name, age } = validData
}
 
function validate(data: unknown): FormData {
  // validate data here
  ...
  return data as FormData
}

Tip: Use runtime validation libraries like zod, yup, or io-ts for robust type-safe validation.


DistributiveOmit

TypeScript's built-in Omit does not distribute over union types as you might expect.
Use DistributiveOmit to omit keys from each member of a union:

type DistributiveOmit<T, K extends keyof any> = T extends unknown
  ? Omit<T, K>
  : never

Example:

interface A {
  id: string
  label: string
  a: string
}
interface B {
  id: string
  label: string
  b: string
}
type C = A | B
// This does NOT work as expected; it behaves like Omit<A, 'id'>
type D = Omit<C, 'id'>
// Use DistributiveOmit for correct behavior:
type E = DistributiveOmit<C, 'id'>

ArrayElement

Extract the element type from an array or tuple:

type ArrayElement<T> = T extends readonly (infer E)[] ? E : never

Example:

const chars = ['a', 'b', 'c'] as const
type Char = ArrayElement<typeof chars> // 'a' | 'b' | 'c'
 
// Or, using TypeScript's built-in indexed access:
const char: (typeof chars)[number] = 'a'

Extend Third-party Module Interface

To extend or augment types from a third-party module, use module augmentation:

global.d.ts
import 'module-name'
declare module 'module-name' {
  export interface ToBeExtended {
    prop: unknown
  }
}

Tip: This is useful for adding custom properties to types from libraries like Express, React, or Next.js.


Utility Types

Some other handy utility types:

  • Partial<T>: Makes all properties in T optional.
  • Required<T>: Makes all properties in T required.
  • Readonly<T>: Makes all properties in T readonly.
  • Record<K, T>: Constructs an object type with keys of type K and values of type T.
  • Pick<T, K>: Picks a set of properties from T.
  • Exclude<T, U>: Excludes from T those types that are assignable to U.
  • Extract<T, U>: Extracts from T those types that are assignable to U.

Type Guards

Type guards help you narrow types at runtime:

function isString(value: unknown): value is string {
  return typeof value === 'string'
}

Example:

function printLength(value: unknown) {
  if (isString(value)) {
    console.log(value.length)
  }
}

Type Inference with as const

Use as const to infer literal types:

const colors = ['red', 'green', 'blue'] as const
type Color = (typeof colors)[number] // 'red' | 'green' | 'blue'

Resources

TypeScript is a powerful tool for writing safer, more expressive JavaScript. Use these patterns and utilities to write robust, maintainable code!