8장 JSX에서 TSX로

리액트 애플리케이션을 타입스크립트로 작성할 때 @types/react 패키지에 정의된 리액트 내장 타입을 사용해본 경험이 있을 것이다. 리액트 내장 타입 중에는 역할이 명확한 것도 있지만, 역할이 비슷해 보이는 타입도 존재하기 때문에 어떤 것을 사용해야 할지 헷갈릴 때가 있 다. 이 절에서는 헷갈릴 수 있는 대표적인 리액트 컴포넌트 타입을 살펴보면서 상황에 따라 어떤 것을 사용하면 좋을지 그리고 사용할 때의 유의점은 무엇인지 알아보자.

8.2 함수 컴포넌트 타입

interface WelcomeProps {
    name string
}

class Welcome extends React.Component<WelcomeProps> {
/* ... 생략 */
}

// 함수 선언을 사용한 방식
function Welcome (props WelcomeProps) JSX.Element {}

// 함수 표현식을 사용한 방식 - React.FC 사용
const Welcome React.FC<WelcomeProps> = ({ name }) => {}

// 함수 표현식을 사용한 방식 - React.VFC 사용
const Welcome React.VFC<WelcomeProps> = ({ name }) => {};

// 함수 표현식을 사용한 방식 - JSX.Element를 반환 타입으로 지정
const Welcome = ({ name } WelcomeProps) JSX.Element => {};

type FC<P = {}> = FunctionComponent<P>;

interface FunctionConiponent<P = {}> {
    // props에 children을 추가
    (props PropsWithChildren<P>, context? any) ReactElementony, any> | null;
    propTypes?: WeakValiclationMap<P> | undefined;
    contextTypes? ValidationMap<any> | undefined;
    defaultProps?: Partial<P> | undefined;
    displayName? string | undefined;
}

type VFC<P = {}> = VoidFunctionComponent<P>;

interface VoidFunctionComponent<P = {}> {
    // children 없음
    (props P, context?; any): ReactElementony, any> | null;
    propTypes?: WeakValidationMap<P> | undefined;
    contextTypes? ValidationMap<any> | undefined;
    defaultprops? Partial<P> | undefined;
    displayName? string | undefined;
}

8.3 Children props 타입 지정

type PropsWithChildren<P> = P & { children? ReactNode | undefined };
type ReactNode = ReactElement | string | number | boolean | null | undefined | ReactNode[];
// example 1
type WelcomeProps = {
    children: "천생연분" | "더 귀한 분" | "귀한 분" | "고마운 분";
}
// example 2
type WelcomeProps = {
    children: string;
}
// example 3
type WelcomeProps = {
    children ReactElement;
};

8.4 render 메서드와 함수 컴포넌트의 반환 타입 - React.ReactElement vs JSX.Element vs ReactReactNode

interface ReactElement<P = any,
    T extends string | JSXElementConstructor<any> =
    | string
    | JSXElementConstructor<any>
> {
    type T;
    props P;
    key: Key | null;
}
// JSX
const element = <div className="container">Hello, world!</div>;
// React.createElement 함수로 반환된 ReactElement
const element = {
  type: 'div',                        // 요소 타입 (HTML 태그 'div')
  props: {                            // 요소에 전달된 속성
    className: 'container',
    children: 'Hello, world!',
  },
  key: null,                          // 리스트에서 사용되는 고유한 키 (현재는 null)
  ref: null,                          // 참조 (ref) 값 (현재는 null)
};
declare global {
    namespace JSX {
        interface Element extends React.ReactElement<any, any> {}
    }
}
type ReactText = string | number;
type ReactChild = ReactElement | ReactText;
type ReactFragment = {} | Iterable<ReactNode>;
type ReactNode =
    | ReactChild
    | ReactFragment
    | ReactPortal
    | boolean
    | null
    | undefined;

8.5 ReactElement, ReactNode, JSX.Element 활용하기

8.5.1 ReactElement

const element = React.createElement(
    "hl",
    { ClassName "greeting" },
    "Hello, world!"
);

// 주의: 다음 구조는 단순화되었다
const element = {
        type "hl",
    props {
        className "greeting",
        children "Hello, world!",
    },
};

declare global {
    namespace JSX {
        interface Element extends React.ReactElement<any, any> {
            // ...
        }
        // ... 
    }
}

8.5.2 ReactNode

8.5.3 JSX.Element

8.6 사용 예시

8.6.1 ReactNode

type ReactNode = ReactElement | string | number | boolean | null | undefined | ReactNode[];
interface MyComponentProps {
    children? React.ReactNode;
    // ...
}

8.6.2 JSX.Element

interface Props {
    icon JSX.Element;
}

const Item = ({ icon }: Props) => {
    // prop으로 받은 컴포넌트의 props에 접근할 수 있다
    const iconsize = icon.props.size;
    return (<li>{icon}</ll>);
};

// icon prop에는 JSX.Element 타입을 가진 요소만 할당할 수 있다
const App = 0 => {
    return <Item icon={<Icon size={14} />} />
}

8.6.3 ReactElement

interface IconProps {
    size number
}
interface Props {
    // ReactElement의 props 타입으로 IconProps 타입 지정
    icon React.ReactEleinent<IconProps>;
}

const Item = ({ icon } Props) => {
    // icon prop으로 받은 컴포넌트의 props에 접근하면, props의 목록이 추론된다
    const iconsize = icon.props.size;
    return <li>{icon}</li>;
};

8.7 리액트에서 기본 HTML 요소 타입 활욤하기

 const SquareButton = () => <button>정사각형 버튼</button>;

8.7.1 DetailedHTMLProps와 ComponentWithoutRef

type NativeButtonProps =React.DetailedHTMLProps<
    React.ButtonHTMLAttributes<HTMLButtonElement>,
    HTMLButtonElement
>;

// ButtonProps의 onClick 타입은 실제 HTML button 태그의 onClick 이벤트 핸들러 타입과 동일
type ButtonProps = {
    onClick? NativeButtonProps["onClick"];
}
type NativeButtonType = React.ComponentPropsWithoutRef<"button">;
// 마찬가지로 리액트의 button onClick 이벤트 핸들러에 대한 타입이 할당
type ButtonProps = {
    onClick? NativeButtonType["onClick"];
}

끝!