본문으로 바로가기

typescript에서 DOM 다루기

category Programming Language/🟦 Typescript 2021. 3. 1. 23:46

typescript-kr.github.io/pages/tutorials/dom-manipulation.html

 

TypeScript 한글 문서

TypeScript 한글 번역 문서입니다

typescript-kr.github.io

 

사실, 배울 필요 없이 MDN보면 다 나와있다.

 

프론트 프레임웤, 특히 React에서 직접 DOM을 다루는 것은 잘못된 방식이지만, 그 외의 상황에선 당연히 DOM을 다룰 수 있습니다.

DOM과 Event에 대한 타입은 lib.dom.d.ts에 정의되어 있습니다. IDE 상에서 보고 싶으면 Element, HTMLElement 등을 검색하여 살펴봅시다.

솔직히, 외울 필요없이 MDN 문서를 뒤져보면, 타입에 대한 위계를 살펴볼 수 있습니다.

developer.mozilla.org/en-US/docs/Web/API

 

예를 들어, video태그를 사용한다고 하면, HTMLVideoElement를 타입으로 사용하고 싶을 경우

해당 타입의 위계가 HTMLMediaElement, HTMLElement, Element, Node로 이어지는 것을 확인할 수 있습니다.

 

 

 

그러나, 역시 자주 사용하는 것은 외워지기 마련입니다. 아래 글에서는 자주 사용하는 타입을 다뤄보았습니다.

 

 

1. 간단히 Element 타입 살펴보기, querySelector

Elment는 돔을 이루는 기본이고, 이를 extends한 것으로 이루어져 있습니다.

* 물론 Element가 형태소인 것은 아닙니다. 많은 내용들을 extends한 것을 lib.dom.d.ts에서 살펴보실 수 있습니다.
interface Element extends Node, Animatable, ChildNode, InnerHTML, NonDocumentTypeChildNode, ParentNode, Slottable

Element

  > HTMLElement

  > HTMLParagraphElement, HTMLSpanElement, HTMLDivElement 등등

 

 

등 구체적인 엘레먼트들 존재합니다. 당연히 HTML 태그 별 속성이 다르므로 해당 타입에 정의된 속성도 다릅니다. 

<p class="dom"></p>

 

평소 js에서 하던대로 작성하면  Property 'innerText' does not exist on type 'Element' 에러가 발생합니다.

즉, myDom은 Element로 추론이 되었고, Element 타입에는 innerText가 없다는 겁니다.

const myDom = document.querySelector(".dom");
myDom.innerText = "someword"; // Error!

 

따라서, innerText를 가진 HTMLParagraphqElement 타입을 assersion 해주어야 합니다.

// 아래 같은 방식은 에러가 남. querySelector 함수 결과값 타입이 Element이기 때문
const myDom: HTMLParagraphElment = document.querySelector(".dom")

// 타입 단언으로 타입 에러 해결 가능
const myDom = document.querySelector(".dom") as HTMLParagraphElement; 

 

createElement, querySelector, querySelectorAll 등의 메서드를 확인해보면, 어떤값을 반환하는지에 대한 정의가 되어 있습니다.

정의를 외울 필요는 없지만, 커서를 호버에서 함수가 무엇을 리턴하는지 확인하고, 필요시 타입 단언을 통해서 타입 에러를 해결할 수 있어야 합니다.

/**
 * 선택자와 일치하는 노드의 자식 중 첫 번째 요소를 반환합니다.
 */
querySelector<K extends keyof HTMLElementTagNameMap>(selectors: K): HTMLElementTagNameMap[K] | null;
querySelector<K extends keyof SVGElementTagNameMap>(selectors: K): SVGElementTagNameMap[K] | null;
querySelector<E extends Element = Element>(selectors: string): E | null;

/**
 * 선택자와 일치하는 모든 노드 자식 요소를 반환합니다.
 */
querySelectorAll<K extends keyof HTMLElementTagNameMap>(selectors: K): NodeListOf<HTMLElementTagNameMap[K]>;
querySelectorAll<K extends keyof SVGElementTagNameMap>(selectors: K): NodeListOf<SVGElementTagNameMap[K]>;
querySelectorAll<E extends Element = Element>(selectors: string): NodeListOf<E>;

 

2. Event 타입의 위계 및 eventListener overload 에러 해결

 

Elment와 HTMLElemnt의 위계가 존재하는 것처럼, Event에도 위계가 존재합니다.

 

Event

  > UIEvent

  > MouseEvent, TouchEvent, FocusEvent, KeyboardEvent, InputEvent 등

developer.mozilla.org/en-US/docs/Web/API/UIEvent

 

Event 관련 작업을 하다보면, overload 에러가 자주나게 됩니다.

tsconfig.js에서 strictFunctionTypes로 설정한 경우, 이벤트 리스너에서 에러를 자주 뱉습니다.

 

가장 대표적인 예를 들어보면 다음과 같습니다.

myDom.addEventListener('click', handleClick); // Error!

function handleClick(event: MouseEvent) {
  // ...logic
}

 

addEventListner를 호버해서 타입을 살펴보면 콜백 함수의 인자로 Event 를 받기를 원하고 있습니다.

아래처럼 바꿔주면 타입에 맞아 타입 에러를 회피할 수 있습니다.

function handleClick(event: Event) {
  // ...logic
}

darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체