TypeScript with React

This section documents React 19.x for the web, using function components and Hooks throughout (class components appear only where React still requires them, such as error boundaries). React Native is out of scope and gets only a pointer. The React 19 additions follow the current official React documentation. This content was generated with the assistance of AI and should be verified against react.dev before being relied on in production, since React APIs continue to evolve between releases.

This section’s bibliography lists the reference material consulted while preparing these pages.

TypeScript catches whole classes of React bugs — a missing prop, the wrong shape passed to a component, an event field that does not exist. Use the .tsx extension and [source,tsx] for anything where types are the point. See Using TypeScript. This page covers the React-specific typing patterns only; for the language itself — the type system, generics, the utility types, and tsconfig — see the TypeScript Reference.

Typing props

Describe props with an interface or a type alias; either works, interface is conventional for object props. Type children as React.ReactNode.

interface ButtonProps {
  label: string;
  onClick: () => void;
  variant?: 'primary' | 'ghost';   // optional
  children?: React.ReactNode;
}

function Button({ label, onClick, variant = 'primary', children }: ButtonProps) {
  return (
    <button className={variant} onClick={onClick}>
      {label}
      {children}
    </button>
  );
}

Typing hooks and events

// useState — inferred from the initial value; annotate when it can be null
const [user, setUser] = useState<User | null>(null);
const [count, setCount] = useState(0);              // number, inferred

// useReducer — type the state and a discriminated-union action
type Action = { type: 'inc' } | { type: 'set'; value: number };
function reducer(state: number, action: Action): number { /* ... */ return state; }

// useRef — DOM ref starts as null
const inputRef = useRef<HTMLInputElement>(null);

// event handlers
function onChange(e: React.ChangeEvent<HTMLInputElement>) {
  setName(e.target.value);
}
function onSubmit(e: React.FormEvent<HTMLFormElement>) {
  e.preventDefault();
}

// context — give createContext a type; guard the null default
const ThemeContext = React.createContext<Theme | null>(null);
function useTheme(): Theme {
  const ctx = React.useContext(ThemeContext);
  if (!ctx) throw new Error('useTheme must be used within ThemeProvider');
  return ctx;
}

Generics, utility types, as const

// a generic component
interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
  return <ul>{items.map((it, i) => <li key={i}>{renderItem(it)}</li>)}</ul>;
}

// PropsWithChildren adds `children?: React.ReactNode`
type CardProps = React.PropsWithChildren<{ title: string }>;

// `as const` narrows a literal — useful for action creators and config
const ROUTES = ['home', 'about', 'contact'] as const;
type Route = (typeof ROUTES)[number];   // 'home' | 'about' | 'contact'

Other useful types: React.ComponentProps<'button'> (the props of a DOM element), React.CSSProperties, React.Dispatch<React.SetStateAction<T>>.

PropTypes was removed from React core in React 19. Runtime prop validation is now the job of TypeScript (compile time) or a schema library. See the React TypeScript Cheatsheet and JavaScript Development for the language itself.