TypeScript Best Practices for React Applications

Practical TypeScript patterns and best practices that will level up your React codebase — from proper typing to advanced generics.

Quach Hoang Anh2 min read
  • TypeScript
  • React
  • Best Practices

TypeScript Best Practices for React Applications

TypeScript has become the standard for serious React applications. Here are some patterns I've found essential for maintaining a clean, type-safe codebase.

1. Prefer Interfaces for Component Props

interface ButtonProps {
  variant: 'primary' | 'secondary' | 'ghost'
  size?: 'sm' | 'md' | 'lg'
  children: React.ReactNode
  onClick?: () => void
}

Interfaces give better error messages and are more performant for the type checker than type aliases for object shapes.

2. Use satisfies for Type-Safe Data

const routes = {
  home: '/',
  about: '/about',
  blog: '/blog',
} satisfies Record<string, string>

The satisfies operator validates the type while preserving the narrower inferred type — you get both safety and autocompletion.

3. Generic Components

interface ListProps<T> {
  items: T[]
  renderItem: (item: T) => React.ReactNode
  keyExtractor: (item: T) => string
}

function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
  return (
    <ul>
      {items.map((item) => (
        <li key={keyExtractor(item)}>{renderItem(item)}</li>
      ))}
    </ul>
  )
}

Generic components give you type inference at the call site — no need to manually specify type parameters.

4. Discriminated Unions for State

type AsyncState<T> =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: T }
  | { status: 'error'; error: Error }

This pattern makes impossible states unrepresentable and gives you exhaustive checking in switch statements.

5. Const Assertions for Static Data

const THEMES = ['light', 'dark', 'system'] as const
type Theme = (typeof THEMES)[number] // "light" | "dark" | "system"

Use as const to derive union types from arrays — single source of truth for both runtime and compile-time values.

Conclusion

These patterns have significantly improved the maintainability and developer experience in my projects. TypeScript's type system is powerful — invest time in learning its advanced features and you'll write more confident code.