Type props chặt giúp bắt lỗi ngay lúc gọi component.
Generics — khi component làm việc với một kiểu dữ liệu bất kỳ mà vẫn giữ liên hệ giữa các prop. Ví dụ list nhận items: T[] và renderItem: (item: T) => ... — TS phải suy ra T từ items để renderItem được type đúng.
function List<T>({ items, renderItem }: {
items: T[]
renderItem: (item: T) => React.ReactNode
}) {
return <ul>{items.map(renderItem)}</ul>
}Discriminated union — khi props thay đổi theo "chế độ", tránh kiểu "tất cả optional" cho phép combo vô lý.
Một field kind làm discriminant, TS thu hẹp (narrow) các field còn lại.
type Props =
| { variant: 'link'; href: string }
| { variant: 'button'; onClick: () => void }
// Truyền variant='link' mà thiếu href → TS báo lỗi; onClick không tồn tại ở nhánh này.Lưu ý: union này khiến <button> không thể vừa có href vừa có onClick — chính là cái ta muốn.
Ngược lại nếu để cả hai optional, caller dễ truyền sai và lỗi chỉ lộ lúc runtime.
Tight prop types catch mistakes at the call site.
Generics — when a component works with any data type while preserving the relation between props. E.g. a list taking items: T[] and renderItem: (item: T) => ... — TS must infer T from items so renderItem is typed correctly.
function List<T>({ items, renderItem }: {
items: T[]
renderItem: (item: T) => React.ReactNode
}) {
return <ul>{items.map(renderItem)}</ul>
}Discriminated union — when props change by "mode", avoiding the "all optional" type that allows nonsense combos.
A kind field is the discriminant; TS narrows the rest.
type Props =
| { variant: 'link'; href: string }
| { variant: 'button'; onClick: () => void }
// variant='link' without href → TS errors; onClick doesn't exist on this branch.Note: this union prevents a <button> from having both href and onClick — exactly what we want.
Leaving both optional lets the caller pass the wrong combo and the bug only surfaces at runtime.