react-deep-dive-example icon indicating copy to clipboard operation
react-deep-dive-example copied to clipboard

[문의] p.247 페이지 문의드립니다!

Open GBAJS754 opened this issue 1 year ago • 2 comments

전자책 초판 발행일 : 전자책 2024년 1월15일 페이지 : 247p 내용 : 고차 함수를 활용한 리액트 고차 컴포넌트 만들어보기 예시에서 타입스크립트 오류가 나네요!

interface LoginProps {
  loginRequired?: boolean;
}

function withLoginComponent<T>(Component: ComponentType<T>) {
  return function (props: T & LoginProps) {
    const { loginRequired, ...restProps } = props;

    if (loginRequired) {
      return <>로그인이 필요합니다.</>;
    }

    return <Component {...(restProps as T)} />; // 'T' 형식은 'IntrinsicAttributes & T' 형식에 할당할 수 없습니다.
  };
}

const Component = withLoginComponent((props: { value: string }) => {
  return <h3>{props.value}</h3>;
});

withLoginComponent<T> 부분을 withLoginComponent<T extends { value: string; }>으로 수정하면 해당 오류가 사라지는데 더 좋은 방법이 있다면 알려주시면 감사하겠습니다ㅎㅎ

항상 예시까지 잘 보고있습니다! 감사합니다!

GBAJS754 avatar May 02 '24 14:05 GBAJS754

안녕하세요~ 먼저 책에 많은 관심 가져주셔서 감사합니다 😢

제안해주신 내용

말씀해주신 방법으로도 수정이 가능합니다만, 제안해주신 방법대로 수정하면 다른 컴포넌트에서는 사용할수 없다는 단점이 있습니다.

import React from 'react'

interface LoginProps {
  loginRequired?: boolean;
}

function withLoginComponent<T extends { value: string; }>(Component: React.ComponentType<T>) {
  return function (props: T & LoginProps) {
    const { loginRequired, ...restProps } = props;

    if (loginRequired) {
      return <>로그인이 필요합니다.</>;
    }

    return <Component {...(restProps as T)} />; 
  };
}

// ok
const Component = withLoginComponent((props: { value: string }) => {
  return <h3>{props.value}</h3>;
});

// Error
const ComponentB = withLoginComponent((props: { number: number }) => {
  return <h3>{props.number}</h3>;
});

원인

이 문제의 원인은 코드상으로는 전혀 문제가 없어 보이지만, 타입스크립트에서 제네릭 타입의 파라미터를 바로 할당할 수 없다는 데에 있는데요. 타입 스크립트는 구조적 타이핑 에 기반하고 있기 때문에, 제네릭타입을 바로 할당하는 것을 금지하고 있습니다.

function wrapValue<T>(value: T = "default"): T {
  return value;
}

위 코드는 에러가 납니다. 왜냐하면 "default"는 string 타입으로 추론되므로, T가 (extends`가 없는한) 모든 타입으로 구체화되서 'default'와 일치한다고 볼 수 없기 때문입니다. (T 가 number가 되는 경우, 저 "default"는 무용지물이 됨.)

그래서 타입스크립트에서는 제네릭 타입에 대한 직접적인 할당을 모든 경우에서 제한하고 있습니다. 위 예제 코드는 아무리봐도 restProps가 T, 즉 자식 컴포넌트의 props 이외에 다른 내용이 올 구석은 없지만, 이러한 제한때문에 에러가 발생하고 있는 것으로 보입니다.

올바른 코드

https://www.typescriptlang.org/play/?jsx=4&ts=5.4.5#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wChTgA7GJKTdJOAGQgHMqAFHMAZzgG9ScOABs2VZAEcArsCIATAPwAuOACMIEEakoBuUgF9ymaZQzAIlOAHdgMABYt2lAMK5IlJNQA8AFThIAB40lPJ8EGoAVkgYAHwAFG7gll4wqsjoMAB0SR6pvgCeYEh+sQCUAkJwRDDSUFYmZjAWVvFg3Dyq-gBkzOKUXBC8FYLCwmiWPPD8ov1SsgoANHBZq0RTg7xwBnAAvHDtQzz6VcLAmHDxYs7zckjyI6djNXVW3rGAOh2AH7WAHuOALuNwQAiq4AUscAlquACabACdNWW8AHpYvoxttyEiXvU4N5cilqAJVll4usYJs+Cg+L4yjt4YjtvojKQJpQpnAsZ4cftbA4nFQWal4m0OqoZgA3FAiaRIVRTKBUVjbCq7WKVYRot72ADMsX4h14WRFYqQBjh6oRhjKuiAA

import React from 'react'

interface LoginProps {
  loginRequired?: boolean;
}

function withLoginComponent<T extends object>(Component: React.ComponentType<T>) {
  return function (props: T & LoginProps) {
    const { loginRequired, ...restProps } = props;

    if (loginRequired) {
      return <>로그인이 필요합니다.</>;
    }

    return <Component {...(restProps as T)} />;
  };
}

const Component = withLoginComponent((props: { value: string }) => {
  return <h3>{props.value}</h3>;
});

이렇게 T 뒤에 extends object를 추가해서 수정해야할 것 같습니다. cc) @wikibook

yceffort avatar May 04 '24 05:05 yceffort

@GBAJS754 님, 책 관심있게 읽어주셔서 감사합니다. 다음 인쇄때 반영하도록 하겠습니다. 🙇‍♂️

yceffort avatar May 04 '24 05:05 yceffort