Type Safety

Write components in TypeScript with `strict` mode on; export precise prop and state types.

Why?  Prevents runtime bugs and enables richer IDE/autocomplete support.

TypeScript isn’t just a nicer autocompletion engine—it’s a contract that catches bugs before they hit the browser. But the safety net only works when you keep the holes small. Turning off strict (or sprinkling any everywhere) lets subtle null, undefined, or shape‑mismatch errors slip through, especially as a codebase grows and contributors change APIs. Treating types as first‑class keeps prop boundaries honest, documents intent, and lets IDEs refactor code confidently.

Correct Example

// ProductCard.tsx – fully typed, strict‑mode friendly
import { ReactNode } from "react";

export type Product = {
  id: string;
  name: string;
  priceCents: number;
  imageUrl: string;
};

interface Props {
  product: Product;
  children?: ReactNode;
}

export default function ProductCard({ product, children }: Props) {
  const price = (product.priceCents / 100).toFixed(2);

  return (
    <article className="rounded-xl border p-4 shadow-sm">
      <img
        src={product.imageUrl}
        alt={product.name}
        className="mb-3 h-40 w-full object-cover"
      />
      <h3 className="text-lg font-semibold">{product.name}</h3>
      <p className="mb-4 text-stone-700">${price}</p>
      {children}
    </article>
  );
}

Why this is good

  • Explicit Product type documents data contract across the app.
  • Optional children is typed as ReactNode—no silent undefined errors.
  • strict: true (in tsconfig.json) ensures missing props or null checks fail at compile time.

Incorrect Example

// ProductCardLoose.tsx – loose typing, no strict mode
export default function ProductCardLoose({ product }) {
  // 🤔 product is `any`
  // Implicit any: could be null, undefined, or misspelled keys
  const price = (product.price / 100).toFixed(2); // runtime crash if price undefined

  return (
    <div className="card">
      <img src={product.img} /> {/* typo: img vs imageUrl */}
      <h3>{product.name}</h3>
      <p>${price}</p>
    </div>
  );
}

// Somewhere else …
<ProductCardLoose product={{ id: 42, title: "Banana" }} />; // passes compile
// Renders undefined props, mis‑named keys, potential NaN price

Key Takeaways

  • Turn on strict: strict: true (and noImplicitAny) forces you to handle nullability and missing props.
  • Define reusable domain types: Central Product, User, etc. prevent shape drift and duplicated interfaces.
  • Never use any as a shortcut: Reach for union, generic, or utility types (Partial, Pick) instead.
  • Prop & state typing = self‑docs: A teammate (or future you) can read prop interfaces faster than scrolling comments.
  • Safer refactors: IDE rename + type‑checking flag mismatches instantly across the whole codebase.