Typing Hooks and State

useState is generic, and most of the time you should let it infer: useState('') gives you a string and a setter for it. Supply the type argument only when the initial value is too narrow for the states that follow — an empty array, or a null placeholder for something that arrives later.

Where useState needs help and where it does notJavaScript
import { useState } from 'react';
interface Todo { id: number; text: string; done: boolean }
export function useTodos() {
  const [query, setQuery] = useState('');          // inferred: string
  const [todos, setTodos] = useState<Todo[]>([]);  // [] alone infers never[]
  const [editing, setEditing] = useState<Todo | null>(null);
  return { query, setQuery, todos, setTodos, editing, setEditing };
}
export function useBroken() {
  const [todos, setTodos] = useState([]);
  const [user, setUser] = useState(null);
  setTodos([{ id: 1, text: 'Ship', done: false }]);
  setUser({ name: 'Ada' });
}
Output
src/hooks.ts(13,13): error TS2322: Type '{ id: number; text: string; done: false; }' is not
  assignable to type 'never'.
src/hooks.ts(14,13): error TS2353: Object literal may only specify known properties, and 'name'
  does not exist in type '(prevState: null) => null'.

Both messages are baffling the first time, and both come from widening. [] widens to never[], an array that can hold nothing. null widens to the type null, leaving the setter's updater-function overload as the only one that could still match — hence a complaint about (prevState: null) => null rather than about the object you passed.

The rest of the Hooks API infers cleanly: useMemo from its callback's return value, useCallback from the function you pass, useEffect from nothing. useEffect does enforce one rule — its callback must return void or a cleanup function, so useEffect(() => fetchData(), []) fails with Type 'Promise<number>' is not assignable to 'void | Destructor'. The fix is a braced body.

An array returned from a custom Hook infers as an array of the union of its elements, which is never useful. as const makes it a readonly tuple, so destructuring gives each name its own type.

as const turns an array return into a tupleJavaScript
function useToggle(initial = false) {
  const [on, setOn] = useState(initial);
  return [on, () => setOn(v => !v)] as const;   // readonly [boolean, () => void]
}

Without it, useToggle() returns (boolean | (() => void))[] and the error lands far from its cause, at the JSX writing onClick={toggleOpen}. Prefer an object return unless callers should rename both values freely.