Typing Hooks

useRef

const inputRef = useRef<HTMLInputElement>(null);
inputRef.current?.focus();          // ?. because it is null before mount

const timerRef = useRef<number | null>(null);   // a mutable value

useReducer with Typed Actions

type State = { count: number };
type Action =
  | { type: 'increment' }
  | { type: 'add'; amount: number }
  | { type: 'reset' };

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case 'increment': return { count: state.count + 1 };
    case 'add': return { count: state.count + action.amount };   // amount is known here
    case 'reset': return { count: 0 };
  }
}

const [state, dispatch] = useReducer(reducer, { count: 0 });
dispatch({ type: 'add', amount: 5 });   // ✓
dispatch({ type: 'add' });              // ✗ error: amount is required

Context

type AuthContextValue = { user: User | null; login: (name: string) => void; logout: () => void };

const AuthContext = createContext<AuthContextValue | null>(null);

export function useAuth() {
  const ctx = useContext(AuthContext);
  if (!ctx) throw new Error('useAuth must be used inside <AuthProvider>');
  return ctx;               // type is AuthContextValue, never null
}