Total Typescript - JSX.Element

김동하·2025년 4월 9일
0

typescript

목록 보기
9/21
post-thumbnail

1. React.JSX

리액트 엘리먼트의 타입을 지정할 때 자주 쓰이는 3가지 타입이다.

아래 타입들 쓸 때마다 헷갈렸는데(ReactNode가 다 포함해서 ReactNode를 썼다) 3가지 타입에 대해 알아보자

먼저 JSX.Element다. Element을 타고 index.d.ts 파일로 가보면 Element의 타입이 지정되어 있는 것을 볼 수 있다.

여기서 재밌는 점은 ElementReactElement로 확장되어 있다는 것

즉, 아래와 같이 JSX.ElementReact.ReactElement는 같은 타입을 바라보고 있다.

그럼 ReactNode를 보자.

ReactNode는 대부분의 타입을 커버한다. ReactElement까지 포함하는 것을 확인할 수 있다.

이제 아래와 같이 평범한 컴포넌트를 확인해보자.

타입스크립트는 리턴 타입을 JSX.Element로 추론한다.

그렇담 JSX의 타입은 무엇인가. Reactnamespace를 살펴보자. index.d.ts에 들어가보면 namespace React가 있다.

그리고 대략 4천줄 쯤에 JSX가 있는데, 이 namespace안에 Element가 또 있다!

ElementGlobalJSXElement라는 타입을 확장하고, GlobalJSXElement 가 뭔지 타고 들어가보면

그런데 GlobalJSXElement는 다시 JSX.Element를 확장하고 있다.(순환참조아녀?)

아무튼 정리하자면 React.JSX.Element는 결국 JSX.Element와 같음을 알 수 있다.

이제 다음 컴포넌트를 보자. 리턴 타입이 ReactNode인 컴포넌트는 대부분의 타입을 포함하기 때문에 아래처럼 string을 리턴해도 컴파일 에러가 나지 않는다.

그리고 마지막으로 리턴 타입이 ReactElement인 경우를 보면 앞서 말했든 JSX.ElementReactElement은 같기 때문에 ReactElement도 오직 JSX만 리턴할 수 있다.

결론은 JSX.ElementReactElement 같고 ReactNode는 더 넓은 타입이다.

2. forcing type with children

흔히 사용하는 SelectOption 컴포넌트다.

OptionOptionType 타입을 강제하고 있다.

OptionType에는 __brand라는 프로퍼티가 있는데, __brand 는 간단하게 말해 개발자가 지정한 타입 외 다른 타입의 호환을 막는 역할을 한다.

하지만, 이렇게 만든 SelectOption 을 사용하려고 하면 에러가 나온다

as OptionType 으로 타입을 강제했음에도 Option 컴포넌트가 ReactElement 타입을 반환하여 타입이 불일치 한다는 것이다.

언뜻 보기에 OptionType 잘 반환하는 거 같은데 뭐가 문제일까?

먼저, <Option> 컴포넌트를 변수에 할당해보자

할당된 변수의 타입은 JSX.Element로 나오고 있다. 즉, 제대로 추론을 못하고 있다는 뜻

하지만 컴포넌트가 아닌 함수로 호출하게 되면 제대로 타입을 추론한다.

그래서 문제를 해결할 수 있는 방법은 컴포넌트를 함수로 호출하는 것이다.

하지만 JSX 엘리먼트를 함수로 호출하는 건 아주 잘못된 행동이다.

리액트가 컴포넌트 인스턴스로 인식하지 못하여 Reconciliation를 수행하지 못하기 때문에 매번 호출해버린다.

아쉽게도 children props에 강제로 타입을 지정하는 방법은 없다.

리액트는 모든 JSX 요소를 JSX.Element로 추론하며, 커스텀 타입을 무시하기 때문!

참고 : total typescript

profile
프론트엔드 개발

0개의 댓글