😂왜 DOM요소는 as로 타입단언해야하나요?
사실 별 생각없이 당연히 그런거아님? 하고 넘어갔던 문제인데
디스코드에 질문이 올라온걸 보고 다른사람도 납득할 수 있게 근거를 들어 설명하려니
꽤나 어렵다는 생각이 들었습니다.
따라서 제 나름대로 접근하여 결론을 내봤습니다.
물론 100% 뇌피셜은 아니고 나름대로 레퍼런스를 찾아보면서 근거를 확인했어요
그래도 제 생각이 틀릴 수 있으니 만약 그런 경우 댓글 남겨주시면 감사하겠습니다.
😉나의 답변
익명보호를 위해 질문자 분 성함은 그림판으로 가렸습니다.
우선 이 답변이 제 결론이며 이 포스트에서는 제가 결론에 도달하기까지의 과정을 서술하겠습니다.
🤗타입스크립트로 작성한 파일은 어떻게 컴파일될까?
1. 타입스크립트 컴파일러에 의한 타입스크립트 -> 자바스크립트 변환
2. 바벨 등 트랜스파일러에 의한 자바스크립트 -> 자바스크립트 변환
이 과정에서 바벨 등 트랜스파일러를 통한 변환은 선택적으로 이루어집니다.
😐왜 타입스크립트는 DOM 요소를 확신할 수 없을까?
이는 DOM 요소가 브라우저에 존재하기 때문입니다.
🙄타입스크립트는 런타임에 영향을 주지않는다?
타입스크립트는 일반적으로 런타임에 영향을 주지 않습니다.
일반적이라 표현하는 이유는 예컨대 enum을 사용하는 경우 번들러에 따라 런타임에 영향을 주는 경우도 있기 때문입니다.
https://yceffort.kr/2022/03/typescript-use-union-types-instead-enum
또 타입스크립트의 기능 중 하나인 추상클래스를 사용하는 경우에도
컴파일 이후에 추상클래스와 관련한 코드가 남아있을 수 있습니다.
하지만 이러한 특수한 경우를 제외하면 타입스크립트는 런타임오버헤드를 최소화하는 것을 지향하며
런타임에 타입 체크를 수행하지 않습니다.
즉 타입스크립트가 컴파일을 수행하는 시점에서
타입스크립트는 DOM 요소가 있을지 없을지 정확히 추론할 수 없습니다.
😐그러면 그동안 타입단언이 필요했던 이유도..?
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import './index.css';
import { Provider } from 'react-redux';
import store from './modules/index.ts';
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<Provider store={store}>
<App />
</Provider>,
);
이 코드는 vite이 제공하는 탬플릿의 엔트리포인트입니다.
읽어보면 'root'라는 아이디를 가진 element를 가져오는 코드인데
document.getElementById() 는 셀렉되는 요소가 있으면 그 HTMLElment를 반환하고
그렇지 않으면 null을 반환합니다.
즉 document.getElementById()함수의 반환값은 null|HTMLElement라고 볼 수 있습니다.
그런데 개발자 입장에서는 당연히 Id가 root인 요소를 만들어두었을테니
null일 가능성이 없다는 것을 알고 있습니다.
하지만 타입스크립트 컴파일러는 그 사실을 알 수 없는 것 입니다.
따라서 개발자는 as 문법을 통해 타입단언을 하여
확실히 HTMLElement라는 것을 알려줍니다.
🤩그런데 만약 타입스크립트가 아니었으면 문제가 안될까?
이게 헷갈린 이유는 useEffect의 동작 시점이 언제인지 헷갈렸기 때문입니다.
다행히 useEffect의 동작 시점은 같은 혼란을 느낀 많은 사람들이
보기좋게 정리를 해둔 것들을 찾아 볼 수 있었습니다.
저는 아래 링크를 참고했습니다.
https://charles098.tistory.com/176
https://www.howdy-mj.me/react/useEffect-and-useLayoutEffect
useEffect는 위 게시물에서도 알 수 있듯이
paint까지 완료된 다음 비동기적으로 실행되게 됩니다.
즉 정상적으로 마운트가 되었다면 useRef는 HTMLElement와 연결될 것이고
그런 다음 useEffect가 실행되게 될테니 자바스크립트에서는 문제없이 동작할 것입니다.
이것이 자바스크립트에서는 문제없이 동작할 수 있는 이유라고 생각했습니다.
(이전에 쓴 글인데 잘못된 부분이 있어 추가합니다.)
useEffect의 동작시점은 "대체로" paint 이후이지만 항상 paint 이후 실행이 보장되지는 않습니다.
예컨대 useLayoutEffect가 리렌더링을 유발하는 경우 useEffect는 paint 이전에 실행될 수 있습니다.
😥결론
당연하다는 듯이 사용하고 있던 문법도
왜? 그런걸까?를 생각하기 시작하면 꽤나 설명하기 어렵다..
'typescript' 카테고리의 다른 글
ts-pattern을 사용하여 선언적으로 분기 관리하기 (1) | 2023.11.26 |
---|---|
typescript any 와 unknown의 차이 (0) | 2023.08.13 |
useState가 반환하는 setState의 타입은 어떻게 설정할까? (0) | 2023.05.14 |
Typescript의 컴파일 과정과 성능 (0) | 2023.04.27 |
TypeScript interface cheat sheet (0) | 2023.04.19 |