본문 바로가기

HTML ⁄ CSS ⁄ JS

[이펙티브 타입스크립트] 7장 코드를 작성하고 실행하기

이펙티브 타입스크립트 정주행 스터디 7주차

 

53 TS보다는 ECMAScript 기능 쓰기

2010년으로 거슬러 올라가 자바스크립트가 그리 발전하지 않았을 때, 타입스크립트는 자바스크립트에 없는 기능을 지원했었다.

자바스크립트가 충분히 발전한 지금은, 타입스크립트와 자바스크립트의 경계를 모호하게 만들지 않기 위해서 타입스크립트만의 독자적인 기능은 사용하지 않는 것이 좋다.

열거형(enum), 클래스의 매개변수 프로퍼티, 네임스페이스와 트리플 슬래시 임포트(///), 데코레이터(decorator) 등이 그 예에 해당한다.

정리하자면, 타입스크립트는 타입 체크를 위해 사용하고, 여타 기능은 모두 자바스크립트를 통해 사용함으로써 각 언어의 경계와 목적을 명확하게 하는 것이 좋다.

 

54 객체 순회 노하우

타입스크립트를 처음 사용하다보면, 객체 순회 시 '인덱스 시그니처'와 관련된 에러를 발견할 수 있다.

const obj = {
  one: '하나',
  two: '둘',
  three: '셋',
}

for (const key in obj) {
  cons value = obj[key];
               // obj에 인덱스 시그니처가 없어, 엘리먼트는 암묵적으로 'any' 타입입니다.
}

이 때 key는 string 타입으로 추론된다. key가 사실은 'one', 'two', 'three'를 가리킨다는 것을 표시해서 문제를 해결해보자.

let key: keyof typeof obj; // 'one' | 'two' | 'three' 타입

for (key in obj) {
  const value = obj[key]; // 정상
}

굳이 let을 쓰지 않고도, 간결하게 객체를 순회할 수 있는 방법이 있다. 바로 Object.entries를 사용하는 것이다. 이 방법이 가장 일반적인 방법이라고 한다.

function foo(abc: ABC) {
  for (const [key, value] of Object.entries(abc)) {
    key; // string 타입
    value; // any 타입
  }
}

 

55 DOM 계층구조 이해하기

DOM과 관련된 타입들은 특정한 위계를 형성하고 있다. 그 위계는 EventTarget > Node > Element > HTMLElement > HTMLButtonElement 와 같다.

이번 아이템에서는 각 위계의 타입들을 어떻게 사용해야 바르게 사용한 것인지 익혀두자.

function handleDrag(e: Event) {
  const $target = e.currentTarget; // 'EventTarget' | null
  
  $target.classList.add('dragging'); 
  // 에러
  // $target 개체가 'null'인 것 같습니다.
  // 'EventTarget'형식에 'classList' 속성이 없습니다.
  
  const dragStart = [e.clientX, e.clientY]; 
  // 에러
  // 'Event' 형식에 'clientX' 속성이 없습니다.

  // ...
}

const $surface = document.getElementById('surface');

$surface.addEventListener('mousedown', handleDrage);
// 에러, 개체가 'null'인 것 같습니다.

 

 

1) EventTarget
DOM 타입 중 최상위 타입인 EventTarget은 addEventListener, RemoveEventListener 등 밖에 할 수 없다. 예제에서 currentTarget은 실제로 HTMLElement 타입이지만 타입관점에서 EventTarget이 될 수 있다.

2) Node
다음으로 Node 타입은 Element 타입을 포함하지만 Element가 아닌 Node 도 있다. 텍스트 노드와 주석이 그 예이다.

3) Element
그 다음으로는 Element 타입이 있다. Element 타입에는 HTMLElement와 SVGElement 가 포함된다.

4) HTMLElement
마지막으로 HTMLElement는 HTML***Elment를 포함한다.

그런데 getElementById 등으로 노드를 엘리먼트를 선택하는 경우, 타입스크립트 보다 개발자가 더 정확하게 어떤 HTML***Element인지 알 수 있기 때문에 as 로 타입 단언문을 사용해도 괜찮다.

document.getElementById('main-div') as HTMLDivElement

또한, 이벤트객체 e는 Event 보다는 MouseEvent, KeyboardEvent 처럼 구체적으로 적어주는 것이 좋다.

 

리액트와 함게 타입스크립트를 사용 중이라면 다음과 같이 사용할 수 있다.

import { HTMLButtonAttributes } from 'react';

interface Props extends HTMLButtonAttributes<HTMLButtonElement> {
  isVisible: boolean;
  opacity?: number;
}

 

 

56 정보를 감추는 목적으로 private 사용하지 않기

타입스크립트에서 private, protected, public과 같은 키워드를 붙이더라도 컴파일 후에는 이런 TS 키워드가 모두 사라진다.

즉, private 키워드를 사용하더라도 타입스크립트 상에서 에러를 표시해주는 역할만 하고, 클래스 외부에서 여전히 접근 가능하다.

클래스 외부에서 접근하지 못하게 하고 싶다면 해시(#)를 붙이는 방법이 있다. (private field

class ClassWithPrivateField {
  #privateField;
}

class ClassWithPrivateMethod {
  #privateMethod() {
    return 'hello world';
  }
}

class ClassWithPrivateStaticField {
  static #PRIVATE_STATIC_FIELD;
}

class ClassWithPrivateStaticMethod {
  static #privateStaticMethod() {
    return 'hello world';
  }
}

 

57 소스맵으로 타입스크립트 디버깅하기

소스맵(Source Map)이란, 개발자가 작성한 '원본 코드'에서의 위치와 각종 전처리, 컴파일 과정을 거쳐 자바스크립트 엔진이 실행하는 '변환된 코드'를 서로 매핑하는데 사용되는 map이다.

디버거는 자바스크립트가 실행되는 시점, 즉 런타임에 동작한다. 그렇다보니 원활한 디버깅을 하기 위해서는 소스맵의 도움을 받아야 한다. 대부분의 브라우저, IDE에서 소스맵을 볼 수 있다.

타입스크립트에서는 tsconfig.json에서 소스맵 옵션을 켜두면 된다. 그러면 하나의 .ts 파일에 대해 .js 파일과 .js.map 파일이 생성된다. 브라우저 디버거는 .js파일과 .js.map 파일로 index.ts 파일을 표시해준다.

{
  "compilerOptions": {
    "sourceMap": ture,
    ...
  },
  ...
}

개발환경이 아닌 프로덕션 환경에서 소스맵이 공개되지 않도록 주의하자!