사실 굉장히 쉽게 접할 수 있으면서도 한번 짚고 넘어가면 그 이후로는 접하지 않을 문제지만, 타입스크립트의 기본 개념과 접하는 오류이기 때문에 정리를 하고자함 !
구현된 코드의 의도
1. 부모 컴포넌트에서string[]타입의textList변수를 자식 컴포넌트로 전달
2. 자식 컴포넌트에서는 전달받은string[]타입의 변수를map을 통해 출력
function ParentComponent () { //부모 컴포넌트
const [textList, setTextList] = useState<string[]>([]);
return (
<Box>
<ChildComponent list={textList} />
<Box>
)
}
function ChildComponent (list: string[]){ //자식 컴포넌트
return (
<>
{list.map((listItem, index) => (
<Typography key={index}>{listItem}</Typography>
))}
</>
);
}
위와 같은 코드가 있을 때, 그냥 봤을 때는 문제가 없어보인다.
하지만 이렇게 구현하면 아래와 같은 오류가 나타난다.
💥Type '{ list: string[]; }' is not assignable to type 'IntrinsicAttributes & string[]'.
Property 'list' does not exist on type 'IntrinsicAttributes & string[]'.
오류의 원인은 ChildComponent에서 list라는 값을 props로 받아야 하는데, 현재는 그저 함수의 매개변수로만 정의되어 있기 때문이다. 즉, ChildComponent의 list는 props 객체의 속성인데, 이를 배열처럼 취급하고 있으니 타입이 맞지 않는다는 오류를 나타내는 것이다.
이렇게 말하면 이해가 조금 어려울 수 있으니 아래의 옳은 코드와 함께 이해해보자.
function ParentComponent () { //부모 컴포넌트
const [textList, setTextList] = useState<string[]>([]);
return (
<Box>
<ChildComponent list={textList} />
<Box>
)
}
interface ChildComponentProps{
list: string[]
}
function ChildComponent ({ list }: ChildComponentProps){ //자식 컴포넌트
return (
<>
{list.map((listItem, index) => (
<Typography key={index}>{listItem}</Typography>
))}
</>
);
}
정말 단순하다 !
자 이제 문제의 코드와 옳은 코드를 비교하여 설명해보자면 ...
function ChildComponent(list: string[]){
return ( ... )
React 컴포넌트는 props를 객체로 받기 때문에, ChildComponent에서 list를 받아올 때도 객체 형태로 받아야 한다. 즉, list는 props 객체의 속성으로 전달된다.
props로 전달되는 데이터가 객체 형태임에도 불구하고, 이 방식은 배열로 처리하려 하기 때문에 TypeScript는 list가 배열이 아니라 props 객체라고 인식하게 된다.
-> 결국 위처럼 입력하면 React 컴포넌트가 아닌 일반 함수처럼 동작한다.
interface ChildComponentProps{
list: string[]
}
function ChildComponent ({ list }: ChildComponentProps){
return ( ... )
이 방식은 React에서 props를 받는 정확한 방식이다. 여기서 ChildComponent는 React 컴포넌트로 동작하며, props는 객체 형태로 전달된다. 이때 list는 ChildComponentProps로 타입이 정의된 객체의 속성으로 받아야한다.
-> React 컴포넌트로서 정상 작동