deep-dive-everything / typescript-with-react

우아한 타입스크립트 with 리액트 스터디
0 stars 0 forks source link

현재 코드에서 children 타입이 넓은 이유를 설명하고 타입 범위를 좁혀주세요. #38

Closed limejin closed 6 days ago

limejin commented 1 week ago

📚 252p 📌 현재 코드에서 children 타입이 넓은 이유를 설명하고 타입 범위를 좁혀주세요. (좁히는 데 특정한 조건은 없습니다. 현재보다 좁아지면 됩니다.)

type PropsWithChildren<P> = P & { children?: ReactNode | undefined };

hotdog1004 commented 1 week ago

children 타입은 ReactNode 또는 undefined 타입으로 정의가 되어있습니다. ReactNode 타입은 ReactElemnt 외 boolean, string 등 여러 타입을 포함하고 있는 타입으로 비교적 넓은 타입을 가집니다. 타입을 좁히기 위해서는 아래 코드와 같이 제네릭을 사용할 수 있습니다.

type MyComponentProps<T> = PropsWithChildren<{
   children?: T | T[]; // T 타입의 자식 요소만 허용 
}>;
kwonhygge commented 1 week ago

children에 사용된 ReactNode는 string, ReactElement 등 여러 타입을 포함하고 있는 타입입니다. 따라서 특정 조건의 타입으로 타입을 좁히고 싶다면 아래와 같이 정의할 수 있습니다.

type PropsWithChildren<P> = P & { children?: React.ReactElement };
samseburn commented 1 week ago

PropsWithChildren에서 children의 타입으로 허용하는 ReactNode는 아래와 같은 타입 정의를 따릅니다.

// @types/react
type ReactText = string | number
type ReactChild = ReactElement | ReactText // ReactElement에는 string, number가 허용되지 않음
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined

이처럼 ReactNodeReactElement 외에도 boolean, number 등 여러 타입을 포함하고 있습니다. 따라서 PropsWithChildren의 children 타입을 제한하여 정의하는 방법으로 허용하는 타입을 좁힐 수 있습니다.

type PropsWithChildren<P> = P & {
 children?: ReactElement | undefined 
}

위와 같이 PropsWithChildren 타입을 재정의했다면, 아래처럼 children에 들어갈 수 있는 타입이 제한됩니다.

interface ComponentProps {
  className?: string
}

const Component = ({ children }: PropsWithChildren<ComponentProps>) => {
  return <div>{children}</div>
}

<Component><div>Hello</div></Component>    // ✅ ReactElement에는 jsx 타입이 허용됨
<Component>text</Component>                // ❌ ReactElement에는 string 타입이 허용되지 않음
<Component>{123}</Component>               // ❌ ReactElement에는 number 타입이 허용되지 않음