0Pricing
React Academy · บทเรียน

คอมโพเนนต์แบบเจเนอริกและชนิดยูทิลิตี

เขียนคอมโพเนนต์แบบเจเนอริกที่นำกลับมาใช้ได้ และใช้ PropsWithChildren, ComponentProps และ Omit

คอมโพเนนต์แบบเจเนอริกและชนิดยูทิลิตี เป็นบทเรียน React Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน React Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส React Academy มีบทเรียนทั้งหมด 4 บทเรียน

เหตุใดจึงต้องใช้คอมโพเนนต์แบบเจเนริก

คอมโพเนนต์แบบเจเนริกปรับใช้กับชนิดข้อมูลที่แตกต่างกันได้โดยยังคงปลอดภัยด้านชนิดอย่างสมบูรณ์ เช่น รายการ ตัวเลือก หรือตารางที่กำหนดชนิดและทำงานกับโครงสร้างข้อมูลใด ๆ

รายการแบบเจเนริกอย่างง่าย

ใช้พารามิเตอร์ชนิด T ในฟังก์ชัน เพื่อสร้างคอมโพเนนต์รายการที่แสดงผลรายการชนิดใดก็ได้

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

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

การจำกัดชนิดเจเนริก

ใช้ extends เพื่อเพิ่มข้อจำกัด ทำให้ TypeScript ทราบว่าชนิดนั้นต้องมีคุณสมบัติใดบ้าง

interface SelectProps<T extends { id: string; label: string }> {
  options: T[];
  value: T | null;
  onChange: (option: T) => void;
}

function Select<T extends { id: string; label: string }>(
  { options, value, onChange }: SelectProps<T>
) {
  return (
    <select value={value?.id} onChange={e => onChange(options.find(o => o.id === e.target.value)!)}>
      {options.map(o => <option key={o.id} value={o.id}>{o.label}</option>)}
    </select>
  );
}

PropsWithChildren

React.PropsWithChildren<P> เพิ่ม children?: React.ReactNode ให้กับชนิดพร็อพส์ใด ๆ ซึ่งมีประโยชน์สำหรับคอมโพเนนต์ตัวห่อ

function Card({ title, children }: React.PropsWithChildren<{ title: string }>) {
  return (
    <div className="card">
      <h2>{title}</h2>
      {children}
    </div>
  );
}

ComponentProps

React.ComponentProps<typeof Component> แยกพร็อพส์ของคอมโพเนนต์ที่มีอยู่ออกมา ซึ่งมีประโยชน์เมื่อต้องการขยายหรือตัวห่อคอมโพเนนต์นั้น

import { Button } from './Button';

type ExtendedButtonProps = React.ComponentProps<typeof Button> & {
  loading?: boolean;
};

function LoadingButton({ loading, children, ...props }: ExtendedButtonProps) {
  return (
    <Button {...props} disabled={loading || props.disabled}>
      {loading ? <Spinner /> : children}
    </Button>
  );
}

ComponentPropsWithoutRef

React.ComponentPropsWithoutRef<'input'> แยกพร็อพส์ขององค์ประกอบ HTML โดยไม่รวมพร็อพส์ ref เหมาะอย่างยิ่งสำหรับการห่อองค์ประกอบ HTML

type InputProps = React.ComponentPropsWithoutRef<'input'> & {
  label: string;
  error?: string;
};

function FormInput({ label, error, ...inputProps }: InputProps) {
  return (
    <div>
      <label>{label}</label>
      <input {...inputProps} />
      {error && <span className="error">{error}</span>}
    </div>
  );
}

การตัดออกและการเลือก

ใช้ Omit<T, Keys> เพื่อลบพร็อพส์ที่ระบุ และใช้ Pick<T, Keys> เพื่อเก็บเฉพาะพร็อพส์ที่ระบุจากชนิดที่มีอยู่

interface UserCardProps {
  id: string;
  name: string;
  email: string;
  avatar: string;
  role: string;
}

// Only expose non-sensitive fields:
type PublicUserCardProps = Omit<UserCardProps, 'email' | 'role'>;
// Or pick exactly what you need:
type AvatarProps = Pick<UserCardProps, 'name' | 'avatar'>;

บางส่วนและจำเป็น

Partial<T> ทำให้พร็อพส์ทั้งหมดเป็นตัวเลือกได้ ซึ่งมีประโยชน์สำหรับแบบฟอร์มอัปเดต ส่วน Required<T> ทำให้พร็อพส์ทั้งหมดจำเป็นต้องมี

interface UserFormData { name: string; email: string; bio: string; }

// For update: only send changed fields
type UserUpdatePayload = Partial<UserFormData>;

// For create: all fields are mandatory
type UserCreatePayload = Required<UserFormData>;

Record สำหรับพจนานุกรมที่กำหนดชนิด

Record<Keys, Value> สร้างชนิดออบเจ็กต์ที่มีชนิดคีย์เฉพาะ เหมาะสำหรับแผนที่ค้นหาและคำแปล

type Status = 'idle' | 'loading' | 'success' | 'error';

const statusLabels: Record<Status, string> = {
  idle: 'Ready',
  loading: 'Loading...',
  success: 'Done!',
  error: 'Something went wrong',
};

ReturnType สำหรับชนิดของฮุก

ใช้ ReturnType<typeof useMyHook> เพื่ออนุมานชนิดค่าที่ส่งคืนของฮุกแบบกำหนดเอง โดยไม่ต้องทำซ้ำชนิดด้วยตนเอง

function useCounter(initial: number) {
  const [count, setCount] = useState(initial);
  return { count, increment: () => setCount(c => c + 1) };
}

type CounterHook = ReturnType<typeof useCounter>;
// { count: number; increment: () => void }

คอมโพเนนต์ตารางแบบเจเนริก

ตารางข้อมูลที่ปลอดภัยด้านชนิดอย่างสมบูรณ์ ซึ่งทำงานกับโครงสร้างแถวใดก็ได้ อนุมานคีย์คอลัมน์ และยังมีการเติมโค้ดอัตโนมัติ

interface Column<T> {
  key: keyof T;
  header: string;
  render?: (value: T[keyof T], row: T) => React.ReactNode;
}

function DataTable<T extends object>({ data, columns }: { data: T[]; columns: Column<T>[] }) {
  return (
    <table>
      <thead><tr>{columns.map(c => <th key={String(c.key)}>{c.header}</th>)}</tr></thead>
      <tbody>{data.map((row, i) => (
        <tr key={i}>{columns.map(c => <td key={String(c.key)}>{c.render ? c.render(row[c.key], row) : String(row[c.key])}</td>)}</tr>
      ))}</tbody>
    </table>
  );
}

ตรวจสอบอย่างรวดเร็ว

ชนิดยูทิลิตีใดใช้ลบคีย์ที่ระบุออกจากอินเทอร์เฟซ TypeScript ที่มีอยู่

สรุปทบทวน

คอมโพเนนต์แบบเจเนริกใช้พารามิเตอร์ชนิดเพื่อคงความปลอดภัยด้านชนิดไว้กับโครงสร้างข้อมูลที่หลากหลาย ชนิดยูทิลิตีอย่าง Omit, Pick, Partial, ComponentProps และ PropsWithChildren ช่วยกำจัดการทำชนิดซ้ำด้วยตนเองและทำให้ API ของคอมโพเนนต์ประกอบใช้ร่วมกันได้มากขึ้น

คำถามที่พบบ่อย

บทเรียน “คอมโพเนนต์แบบเจเนอริกและชนิดยูทิลิตี” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “คอมโพเนนต์แบบเจเนอริกและชนิดยูทิลิตี” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส React Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส React Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “คอมโพเนนต์แบบเจเนอริกและชนิดยูทิลิตี”

เขียนคอมโพเนนต์แบบเจเนอริกที่นำกลับมาใช้ได้ และใช้ PropsWithChildren, ComponentProps และ Omit คุณปฏิบัติ React Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน React Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน React Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “คอมโพเนนต์แบบเจเนอริกและชนิดยูทิลิตี” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน React Academy นี้ได้ไหม

ได้ บทเรียน React Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การกำหนดชนิด Props และชนิดค่าที่คอมโพเนนต์คืนกลับ
  2. การกำหนดชนิดอีเวนต์และ Refs ใน TypeScript
  3. คอมโพเนนต์แบบเจเนอริกและชนิดยูทิลิตี
  4. การกำหนดชนิด Context และ Custom Hooks
← กลับไปที่ React Academy