타입이 붙은 props는 컴포넌트 작성자와 호출자 사이의 계약이야. 계약을 잘 세우면 컴파일러와 IDE가 실수를 먼저 잡아 줘.
Prop 타입을 선언하는 방법
작은 컴포넌트라면 함수 인자에 객체 타입을 바로 적어도 돼. 여러 곳에서 재사용하거나 export해야 한다면 이름 붙인 type이나 interface가 읽기 좋아. Union, intersection, mapped type을 조합해야 할 때는 type이 특히 유연하고, 외부 패키지가 선언을 확장해야 한다면 interface의 declaration merging이 도움이 돼. HOC 같은 헬퍼는 함수 시그니처에서 prop 타입을 추론하기도 해.
children에는 ReactNode를 써
레이아웃 컴포넌트는 보통 children: React.ReactNode를 받아. 문자열, 숫자, JSX, Fragment, 배열, null과 undefined까지 React가 렌더링할 수 있는 값을 포괄해. 단일 문자열만 받아야 하는 명확한 이유가 없다면 더 좁은 타입으로 정상적인 사용을 막지 마.
네이티브 엘리먼트의 prop을 이어받아
직접 만든 Button이 onClick, aria-label, disabled 같은 네이티브 button prop도 받아야 한다면 하나씩 다시 선언하지 마. React.ComponentPropsWithoutRef<'button'>를 확장하면 브라우저 button의 계약을 그대로 가져올 수 있어.
제네릭 컴포넌트는 입력에서 타입을 추론해
<List items={users} renderItem={(user) => ...} />처럼 만들면 TypeScript가 items에서 항목 타입을 추론할 수 있어. TSX에서는 <T>가 JSX로 보일 수 있으므로 코드 블록의 문법처럼 구분해 줘.
Props에는 외부 계약만 담아. 호출자가 무엇을 넘기고 무엇을 기대할 수 있는지 표현하면 돼. 내부 상태와 ref, 파생 값까지 밖으로 드러내지 않으면 리팩터링 범위가 컴포넌트 안에 머물러.
Code
네 패턴 나란히·tsx
// 1. Inline
function Hello1({ name }: { name: string }) {
return <p>Hello, {name}</p>;
}
// 2. 이름 붙인 type alias (일꾼)
type Hello2Props = {
name: string;
count?: number; // optional
};
export function Hello2({ name, count = 0 }: Hello2Props) {
return <p>Hello, {name} (×{count})</p>;
}
// 3. Interface (declaration-merging 친화)
export interface Hello3Props {
name: string;
}
export function Hello3({ name }: Hello3Props) {
return <p>Hello, {name}</p>;
}
// 4. 추론 — 기존 컴포넌트 wrap
export function withSilly<P>(Inner: React.ComponentType<P>) {
return function Sillied(props: P) {
return <Inner {...props} />;
};
}
React.ComponentPropsWithoutRef<'input'>을 확장하고 label과 선택적 error를 더한 Input을 만들어. 내부 label이 input을 감싸고 오류가 있을 때만 설명 문구를 보여 주게 해. 일반 text input, value와 onChange가 있는 제어 input, email과 aria-label을 쓰는 세 호출을 작성해. 네이티브 prop을 다시 나열하지 않아도 모두 컴파일되고, 필수 label 누락과 잘못된 prop 타입은 거부되는지 확인해.
Hint
label, error, className을 먼저 구조 분해하고 나머지 prop을 실제 input에 펼쳐. 직접 만든 prop이 네이티브 prop과 충돌하면 Omit으로 우선순위를 분명히 해.
Progress
Progress is local-only — sign in to sync across devices.