C
React/Types/Lesson 22

TypeScript + React — Component Types, Generic Components

35 min·theory
javascript

TypeScript + React — Component Types, Generic Components

💡 Why Should You Learn This?

🎯 Catch bugs while writing code, reducing post-deployment incidents.
💼 IDE autocomplete speeds up development by 30%.
Refactor code safely when making changes.
🔗 As of 2025, all mid-size and larger companies require TypeScript.
🏢 실무에서는
In practice, you need to clearly define Props types — specifying 'what types this function accepts' — so teammates can use components without making mistakes. Building Generic components lets you manage diverse UI elements like buttons, inputs, and dropdowns with a single codebase, making maintenance much easier.

Concepts

When using TypeScript with React, defining Props types for components and implementing reusable generic components are essential frontend development skills as of 2025. Catching runtime errors at compile time and safely leveraging IDE autocomplete and refactoring significantly improves productivity and maintainability in large-scale projects.

Why Does It Matter?

In practice, it is common to pass incorrectly typed data to component Props, or to miss updating related components when an API response structure changes — leading to runtime errors that only surface at runtime. Additionally, when reusing shared components like Table, Modal, or Form across multiple domains without generics, you end up writing duplicate code per domain, and maintenance costs grow exponentially.

Core Concepts

Typing TypeScript + React components is like "attaching an instruction manual to a LEGO brick." By specifying what Props each component accepts and what shape of data it handles, you enable other developers (or your future self) to compose components safely. Generic components act as "universal templates" — an advanced pattern that lets a single component handle a variety of data types.

Key Points

  • Declare component contracts via Props interfaces
  • Type-safe reusable components using generics
  • Patterns used alongside new type features in React 19
💻 Bad Example — Untyped Components and Overuse of `any`
// ❌ Component without type safety
function UserCard({ user }: any) {
  return (
    <div className="card">
      <h3>{user.name}</h3>
      <p>{user.email}</p>
      <span>{user.age} years old</span>
    </div>
  );
}

// ❌ No type checking when used either
function App() {
  const userData: any = {
    userName: "Kim Developer", // userName instead of name
    mail: "[email protected]", // mail instead of email
    years: 30 // years instead of age
  };
  
  return <UserCard user={userData} />; // Error only discovered at runtime
}
💻 Good Example — 2025 Recommended Type-Safe Components
// ✅ Clear Props Type Definition
interface User {
  id: number;
  name: string;
  email: string;
  age: number;
  avatar?: string; // optional property
}

interface UserCardProps {
  user: User;
  onClick?: (userId: number) => void;
  showAge?: boolean;
}

// ✅ React 19's latest functional component pattern
function UserCard({ user, onClick, showAge = true }: UserCardProps) {
  return (
    <div 
      className="card" 
      onClick={() => onClick?.(user.id)}
      role="button"
      tabIndex={0}
    >
      <h3>{user.name}</h3>
      <p>{user.email}</p>
      {showAge && <span>{user.age} years old</span>}
      {user.avatar && <img src={user.avatar} alt={`${user.name} profile`} />}
    </div>
  );
}

// ✅ Type-safe usage
function App() {
  const userData: User = {
    id: 1,
    name: "Kim Developer",
    email: "[email protected]",
    age: 30
  };
  
  const handleUserClick = (userId: number) => {
    console.log(`User ${userId} clicked`);
  };
  
  return (
    <UserCard 
      user={userData}
      onClick={handleUserClick}
      showAge={false}
    />
  );
}
💻 Advanced Example — Generic Reusable Table Component
// ✅ Generic for a versatile table component
interface Column<T> {
  key: keyof T;
  title: string;
  width?: string;
  render?: (value: T[keyof T], record: T, index: number) => React.ReactNode;
}

interface DataTableProps<T> {
  data: T[];
  columns: Column<T>[];
  loading?: boolean;
  onRowClick?: (record: T, index: number) => void;
  emptyText?: string;
}

// ✅ React 19 + TypeScript 5.x latest patterns
function DataTable<T extends Record<string, any>>({
  data,
  columns,
  loading = false,
  onRowClick,
  emptyText = "No data available"
}: DataTableProps<T>) {
  if (loading) {
    return <div className="loading">Loading...</div>;
  }

  if (data.length === 0) {
    return <div className="empty">{emptyText}</div>;
  }

  return (
    <table className="data-table">
      <thead>
        <tr>
          {columns.map((column) => (
            <th key={String(column.key)} style={{ width: column.width }}>
              {column.title}
            </th>
          ))}
        </tr>
      </thead>
      <tbody>
        {data.map((record, index) => (
          <tr 
            key={index}
            onClick={() => onRowClick?.(record, index)}
            className={onRowClick ? 'clickable' : ''}
          >
            {columns.map((column) => (
              <td key={String(column.key)}>
                {column.render 
                  ? column.render(record[column.key], record, index)
                  : String(record[column.key] ?? '')
                }
              </td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

// ✅ Usage example - User table
interface User {
  id: number;
  name: string;
  email: string;
  role: 'admin' | 'user';
  lastLogin: Date;
}

function UserManagement() {
  const users: User[] = [
    { id: 1, name: "Kim Dev", email: "[email protected]", role: "admin", lastLogin: new Date() }
  ];

  const userColumns: Column<User>[] = [
    { key: 'name', title: 'Name', width: '150px' },
    { key: 'email', title: 'Email', width: '200px' },
    { 
      key: 'role', 
      title: 'Role', 
      width: '100px',
      render: (role: User['role']) => (
        <span className={`badge ${role}`}>
          {role === 'admin' ? 'Admin' : 'User'}
        </span>
      )
    },
    {...
      key: 'lastLogin',
      title: 'Last Login',
      render: (date: Date) => date.toLocaleDateString('ko-KR')
    }
  ];

  return (
    <DataTable<User>
      data={users}
      columns={userColumns}
      onRowClick={(user) => console.log('Selected user:', user.name)}
      emptyText="No registered users"
    />
  );
}

💡 ⚠️ Common Mistakes

  • Making all fields required in a Props interface without distinguishing optional from required, which hurts component reusability
  • Not setting proper extends constraints in generic components, causing undefined errors at runtime
  • Typing event handler or callback function parameters as any, which causes callers to lose type safety

💡 🎯 Interview Prep

Q: Explain how to define Props types in a React component and the differences between interface and type.
Q: In what situations are generic components useful, and how have you used them in real projects?

Hint: Defining Props types → reasons to use interface (extensibility) → distinguishing optional vs. required → use cases for generics (Table, Modal, Form) → improved type safety and code reuse → experience applying them in real projects

⚛️ React Patterns — TypeScript + React — Component Types, Generic Components

Learn step by step, with code examples, how TypeScript + React — Component Types, Generic Components are used in React.
1 🧩 1. Defining a Component
A function is a component
function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>;
}
2 📤 2. Passing Props
Unidirectional data flow from parent to child
<Greeting name="Hong Gil-dong" />
3 🔁 3. Reuse
The same component, used multiple times with different props
<Greeting name="Alice" />
<Greeting name="Bob" />
4 💡 4. Unidirectional Data Flow
When a child needs to update parent state, it receives a callback function via props

🎮 TypeScript + React — Component Types, Generic Components — Step-by-Step Understanding

Click each step to read the content and track your progress with the "Got it" button.
🖥️ Result — rendered React component
✏️ React 코드 수정하기 (클릭해서 열기)
⚛️ React 18 + Babel Standalone — see the result first, then edit the code yourself.

Check Quiz

What is the recommended way to define props types for a React component?
💡 Define as interface Props { name: string; age: number } or type Props = {...}, then use it like function Component({ name, age }: Props). TypeScript catches props type errors at compile time.
TypeScript + React - React