React 컴포넌트의 타입을 결정할 때 ReactNode, JSX.Element 등의 여러 타입이 있다.
각 타입의 차이점을 이해하고, 상황에 맞게 타입을 정의하고자 글을 작성하였다.
interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>>
{
type: T;
props: P;
key: Key | null;
}
const createElement: ReactElement = React.createElement('div', { className: 'name' }, 'React');
const jsx: ReactElement = <div className="name">React</div>;
ReactElement는 React.createElement를 호출하거나 JSX 문법을 사용해서 만든 객체의 인터페이스 이며, 이 객체는 type, props를 가지고 있다.
함수형 컴포넌트는 ReactElement를 반환하고, 클래스형 컴포넌트는 ReactNode를 반환한다.
declare global {
namespace JSX {
interface Element extends React.ReactElement<any, any> {}
}
}
JSX.Element는 props와 type이 any인 제네릭 타입을 가진 ReactElement 이다.
JSX는 global namespace에 있기 때문에 다양한 라이브러리에서 자체 라이브러리의 방법대로 실행될 수 있다.
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
ReactNode는 ReactElement의 superset 이다.
ReactChild, ReactFragment, ReactPortal, boolean, null, undefined 유니언 타입을 가지고 있다.
따라서, children을 사용하는 컴포넌트에서 children의 타입을 ReactNode로 정의해주는 경우가 많다.
ReactNode는 ReactElement의 superset이고, JSX.Element는 제네릭 타입을 가진 ReactElement 이므로, 이 셋의 관계는 위의 이미지와 같다.
type ReactText = string | number;
type ReactChild = ReactElement | ReactText;
ReactChild는 ReactElement, ReactText 유니언 타입을 가지고 있으며,
ReactText는 string, number 타입을 가지고 있다.
interface ReactNodeArray extends Array<ReactNode> {}
type ReactFragment = {} | ReactNodeArray;
ReactFragment는 object, ReactNodeArray 유니언 타입을 가지고 있으며,
ReactNodeArray는 배열 형태의 ReactNode 타입이다.
interface ReactPortal extends ReactElement {
key: Key | null;
children: ReactNode;
}
ReactPortal은 ReactElement를 상속 받아 props, type, children을 가지고 있는 객체의 인터페이스 이다.
children에는 ReactNode 타입이 정의되어 있다.
type ComponentType<P = {}> = ComponentClass<P> | FunctionComponent<P>;
ComponentType은 클래스형 컴포넌트, 함수형 컴포넌트 유니언 타입을 가지고 있으며,
컴포넌트의 props에 대한 제네릭 타입을 정의할 수 있다.