Lesson 33 — Advanced TypeScript Patterns for React (Professional-Level)

🧭 Introduction

In the previous lesson, you learned the fundamentals of TypeScript in React — typing props, state, events, and context.

But senior React developers go further.

They use advanced TypeScript patterns to:

  • Build scalable component APIs
  • Create reusable and flexible components
  • Catch complex bugs at compile time
  • Improve developer experience dramatically

This lesson focuses on practical TypeScript patterns used in real-world React applications, not theoretical complexity.


🎯 What You’ll Learn in This Lesson

By the end of this lesson, you will understand:

  • Advanced prop typing techniques
  • Union and discriminated union patterns
  • Generics in React components
  • Utility types in real React use cases
  • Polymorphic components
  • Common pitfalls and best practices

🧠 Why Advanced TypeScript Matters in React

Without advanced typing:

  • Props become loosely defined
  • Components accept invalid combinations
  • Bugs slip into production

With advanced typing:

  • Invalid usage fails at compile time
  • Component APIs become self-documenting
  • Refactoring is safe and predictable

🧩 Union Types for Props

Union types restrict values to a fixed set.

Example — Status Badge

type Status = "success" | "error" | "warning";

type BadgeProps = {
  status: Status;
};

function Badge({ status }: BadgeProps) {
  return <span>{status}</span>;
}

Now:
<Badge status="loading" />
✅ Compile-time error


🧠 Discriminated Union (Very Important Pattern)

This pattern ensures valid prop combinations.


Example — Alert Component

type SuccessAlert = {
  type: "success";
  message: string;
};

type ErrorAlert = {
  type: "error";
  errorCode: number;
};

type AlertProps = SuccessAlert | ErrorAlert;

function Alert(props: AlertProps) {
  if (props.type === "success") {
    return <p>{props.message}</p>;
  }

  return <p>Error code: {props.errorCode}</p>;
}

✔ Type-safe branching
✔ Impossible invalid combinations


🧠 Generics in React Components

Generics make components reusable across data types.


Example — Generic List Component

type ListProps<T> = {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
};

function List<T>({ items, renderItem }: ListProps<T>) {
  return <ul>{items.map(renderItem)}</ul>;
}


Usage

<List
  items={[1, 2, 3]}
  renderItem={(n) => <li>{n}</li>}
/>

<List
  items={[{ id: 1, name: "Avni" }]}
  renderItem={(user) => <li>{user.name}</li>}
/>

✔ Fully type-safe
✔ Highly reusable


🧠 Typing API Responses with Generics

async function fetchData<T>(url: string): Promise<T> {
  const res = await fetch(url);
  return res.json();
}


Usage

type User = { id: number; name: string };

const users = await fetchData<User[]>("/api/users");

✔ No any
✔ API contracts enforced


🧠 Utility Types (Used in Real Apps)

Partial<T> — Optional Fields

type User = {
  name: string;
  email: string;
};

type UpdateUser = Partial<User>;

Useful for update forms.


Pick<T, K> — Select Fields

type UserPreview = Pick&lt;User, "name">;


Omit<T, K> — Remove Fields

type PublicUser = Omit&lt;User, "email">;


Record<K, T> — Key-Value Maps

type RolePermissions = Record&lt;string, string[]>;


🧠 Polymorphic Components (Advanced Pattern)

Polymorphic components change behavior via props.


Example — Button or Link

type ButtonProps =
  | { as: "button"; onClick: () => void }
  | { as: "link"; href: string };

function Button(props: ButtonProps) {
  if (props.as === "link") {
    return <a href={props.href}>Link</a>;
  }

  return <button onClick={props.onClick}>Button</button>;
}

TypeScript ensures:

  • href only exists for links
  • onClick only exists for buttons

🧠 Typing Custom Hooks with Generics

function useAsync<T>(asyncFn: () => Promise<T>) {
  const [data, setData] = React.useState<T | null>(null);

  useEffect(() => {
    asyncFn().then(setData);
  }, []);

  return data;
}


Usage

const users = useAsync&lt;User[]>(fetchUsers);


🚨 Advanced TypeScript Mistakes to Avoid

❌ Overengineering Types

If types are harder than logic, simplify.


❌ Using any to Escape Errors

Fix the type instead.


❌ Creating God Types

Split large types into smaller ones.


❌ Blindly Copying Complex Patterns

Understand before using.


🎯 Best Practices (Senior-Level)

✅ Use discriminated unions for props
✅ Use generics for reusable components
✅ Type public APIs strictly
✅ Keep types readable
✅ Prefer correctness over cleverness
✅ Let TypeScript guide design


❓ FAQs — Advanced TypeScript in React

🔹 Is advanced TypeScript mandatory?

No, but extremely valuable for large apps.


🔹 Should beginners use these patterns?

Gradually — not all at once.


🔹 Are these patterns used in real companies?

Yes — heavily in design systems and shared libraries.


🔹 Is this asked in interviews?

Yes — especially for senior roles.


🧠 Quick Recap

✔ Union & discriminated union types improve safety
✔ Generics enable reuse
✔ Utility types simplify transformations
✔ Polymorphic components improve APIs
✔ Advanced typing = fewer bugs


🎉 Conclusion

Advanced TypeScript patterns turn React code into:

  • Self-documenting APIs
  • Compile-time safe systems
  • Scalable architectures

Mastering these patterns means:

You’re not just writing React —
you’re designing systems.

This completes SECTION 10 — TypeScript with React 🔷⚛️


👉 Next Section

SECTION 11 — Advanced React Projects

👉 Next Lesson

Project 1 — Advanced Admin Dashboard

Leave a Comment