사실 굉장히 쉽게 접할 수 있으면서도 한번 짚고 넘어가면 그 이후로는 접하지 않을 문제지만, 타입스크립트의 기본 개념과 접하는 오류이기 때문에 정리를 하고자함 !
구현된 코드의 의도
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 컴포넌트로서 정상 작동