0Pricing
TypeScript Academy · Lesson

Custom hooks with generics & inference

Write reusable custom hooks with for values, results, and errors; leverage inference and constraints for safe APIs.

Intro

Goal: Build generic hooks that feel native: a typed localStorage hook, an async loader, and a controlled/uncontrolled helper. You will rely on inference so callers rarely annotate types.

  • Return tuples for React-like ergonomics
  • Use constraints when needed
  • Keep no any

useLocalStorage<T>

Return a [value, set] tuple like useState. Keep T consistent across value and setter.

import { useEffect, useState } from "react"

export function useLocalStorage<T>(key: string, initial: T): [T, (v: T | ((prev: T) => T)) => void] {
  const [value, setValue] = useState<T>(() => {
    try {
      const raw = localStorage.getItem(key)
      return raw ? (JSON.parse(raw) as T) : initial
    } catch {
      return initial
    }
  })

  useEffect(() => {
    try { localStorage.setItem(key, JSON.stringify(value)) } catch {}
  }, [key, value])

  const set = (v: T | ((prev: T) => T)) => {
    setValue(prev => (typeof v === "function" ? (v as (p: T) => T)(prev) : v))
  }

  return [value, set]
}

// Usage
// const [user, setUser] = useLocalStorage("user", { id: 1, name: "Ada" })

All lessons in this course

  1. Custom hooks with generics & inference
  2. Suspense & error boundaries types
← Back to TypeScript Academy