HOMEPORTFOLIOBLOGABOUT
← Back to Blog

TypeScript Best Practices for React Developers

February 1, 2025•3 min read•Tutorial

Learn how to write better, more type-safe React code with TypeScript

TypeScript Best Practices for React Developers

Introduction

TypeScript has become the de facto standard for building modern React applications. In this post, we'll explore best practices for writing type-safe React code.

1. Proper Component Typing

Always type your component props explicitly:

interface ButtonProps {
  label: string;
  onClick: () => void;
  variant?: 'primary' | 'secondary';
  disabled?: boolean;
}

export default function Button({
  label,
  onClick,
  variant = 'primary',
  disabled = false,
}: ButtonProps) {
  return (
    <button
      onClick={onClick}
      disabled={disabled}
      className={`btn btn-${variant}`}
    >
      {label}
    </button>
  );
}

2. Use Type Inference

Let TypeScript infer types when possible:

// Good - type is inferred
const [count, setCount] = useState(0);

// Unnecessary - type is already inferred
const [count, setCount] = useState<number>(0);

3. Discriminated Unions for State

Use discriminated unions for complex state:

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

function useAsyncData<T>(fetchFn: () => Promise<T>) {
  const [state, setState] = useState<AsyncState<T>>({
    status: 'idle',
  });

  // TypeScript now knows the exact shape based on status
  if (state.status === 'success') {
    console.log(state.data); // ✅ data is available
  }

  if (state.status === 'error') {
    console.log(state.error); // ✅ error is available
  }
}

4. Generic Components

Create reusable generic components:

interface SelectProps<T> {
  options: T[];
  value: T;
  onChange: (value: T) => void;
  getLabel: (option: T) => string;
}

export default function Select<T>({
  options,
  value,
  onChange,
  getLabel,
}: SelectProps<T>) {
  return (
    <select
      value={getLabel(value)}
      onChange={(e) => {
        const option = options.find(
          (o) => getLabel(o) === e.target.value
        );
        if (option) onChange(option);
      }}
    >
      {options.map((option) => (
        <option key={getLabel(option)} value={getLabel(option)}>
          {getLabel(option)}
        </option>
      ))}
    </select>
  );
}

5. Strict Event Typing

Type your event handlers correctly:

interface FormProps {
  onSubmit: (data: FormData) => void;
}

export default function Form({ onSubmit }: FormProps) {
  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    onSubmit(formData);
  };

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    console.log(e.target.value);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" onChange={handleChange} />
      <button type="submit">Submit</button>
    </form>
  );
}

6. Utility Types

Leverage TypeScript utility types:

interface User {
  id: number;
  name: string;
  email: string;
  role: 'admin' | 'user';
}

// Pick specific fields
type UserPreview = Pick<User, 'id' | 'name'>;

// Make all fields optional
type PartialUser = Partial<User>;

// Make all fields required
type RequiredUser = Required<User>;

// Omit specific fields
type PublicUser = Omit<User, 'role'>;

7. Avoid any

Always prefer unknown over any:

// Bad
function processData(data: any) {
  return data.value; // No type checking
}

// Good
function processData(data: unknown) {
  if (typeof data === 'object' && data !== null && 'value' in data) {
    return (data as { value: string }).value;
  }
  throw new Error('Invalid data');
}

Conclusion

TypeScript significantly improves the development experience and code quality in React applications. By following these best practices, you'll write more maintainable and type-safe code.

Remember:

  • Type your props explicitly
  • Use type inference when appropriate
  • Leverage discriminated unions for complex state
  • Create generic, reusable components
  • Avoid any - use unknown instead

Happy typing!

#typescript#react#best-practices#type-safety