이펙티브 타입스크립트 8장

정태호·2023년 9월 11일
0

타입스크립트

목록 보기
13/13
post-thumbnail

타입스크립트로 마이그레이션하기

아이템 58. 모던 자바스크립트로 작성하기

모던 자바스크립트란 ? 최신버전의 자바스크립트로, 현재는 ES2015(ES6) 버전부터 모던 자바스크립트라고 부르고 있다.

타입스크립트는 타입체크 기능도 있지만, 타입스크립트 코드를 특정 버전의 자바스크립트로 컴파일 하는 기능도 가지고 있다. 즉 타입스크립트의 컴파일러를 자바스크립트 트랜스파일러로 사용할 수 있다.

ECMAScript

  • ES2015부터는 임포트(import)와 익스포트(export)를 사용하는 ECMASciprt 모듈이 표준이 되었다.

프로토타입 대신 클래스 사용하기

  • 마이그레이션하려는 코드에 단순한 객체를 다룰 때 프로토타입을 사용하고 있었다면 클래스로 바꾸는 것이 좋다.
  • 객체를 프로토타입으로 구현
function Person(first,last){
    this.first = first;
    this.last = last;
}

Person.prototype.getName = function(){
    return this.first + '' + this.last;
}

const marie = new Person('Marie', 'Curie');
const personName = marie.getName();
  • 프로토타입 기반 객체를 클래스 기반 객체로 변경
class Person {
  first: string
  last: string

  constructor(first: string, last: string) {
    this.first = first
    this.last = last
  }

  getName() {
    return this.first + ' ' + this.last
  }
}

const marie = new Person('Marie', 'Curie')
const personName = marie.getName()

클래스로 구현한 Person 객체가 문법이 간결하고 직관적이다.

편집기에서 프로토타입 객체에 마우스를 올려, 타입스크립트 언어 서비스를 통해 간단히 클래스 객체로 변환할 수 있다.

var 대신 let/const 사용하기

  • var 키워드를 사용하면 스코프 규칙에 문제가 생길 수 있다.

for 대신 for-of 또는 배열 메서드 사용하기

  • for-of 루프는 코드가 짧고 인덱스 변수를 사용하지 않기 때문에 실수를 줄일 수 있다.
  • 인덱스 변수가 필요하면 forEach

함수 표현식보다 화살표 함수 사용하기

class Foo {
  method() {
    console.log(this);
    [1, 2].forEach(function(i) {
      console.log(this);
    });
  }
}
const f = new Foo();
f.method();
// Foo, undefined, undefined in strict mode
// Foo, window, window (!) in non-strict mode
  • 화살표 함수 사용
class Foo {
  method() {
    console.log(this);
    [1, 2].forEach(i => {
      console.log(this);
    });
  }
}
const f = new Foo();
f.method();
// Always prints Foo, Foo, Foo

컴파일러 옵션에 noImplicitThis(또는 strict)를 설정하면, 타입스크립트가 this 바인딩 관련오류를 표시해준다.

단축 객체 표현과 구조 분해 할당 사용하기

  • 객체 속성과 변수 이름이 같으므로 단축 객체 표현 사용
const x = 1, y = 2, z = 3;
const pt = {
  x, y, z,
};
  • 구조 분해 할당
const props = obj.props;
const a = props.a;
const b = props.b;

const {props} = obj;
const {a,b} = props;

// 한번에 사용하는 예시
const {props: {a,b}} = obj;

함수 매개변수 기본값 사용하기

  • 모던 자바스크립트에서는 매개변수에 기본값을 직접 지정할 수 있다.
function parseNum(str, base = 10) {
  return parseInt(str, base);
}

코드가 간결해질 뿐만 아니라 base가 선택적 매개변수라는 것을 나타낼 수 있다. 또한 타입 추론도 가능해진다.

저수준 프로미스나 콜백 대신 async/await 사용하기

  • 코드가 간결해져 버그나 실수를 방지할 수 있다.
  • 비동기 코드에 타입 정보가 전달되어 타입 추론을 가능하게 한다.

연관 배열에 객체 대신 Map과 Set 사용하기

인덱스 시그니처는 편리하지만 몇 가지 문제가 있다. 특정 문자열이 주어질 때, 원치 않는 값과 타입을 받을 수 있기 때문에, 이런 문제를 방지하기 위해 Map을 사용하는 것이 좋다.

ex) 문자열이 constructor일 때

  • prototype에 있는 생성자 함수여서 이상한 동작이 발생한다.
function countWordMap(text: string) {
  const counts = new Map<string, number>()
  for (const word of text.split(/[\s,.]+/)) {
    counts.set(word, 1 + (counts.get(word) || 0))
  }
  return counts
}

타입스크립트에 use strict 넣지 않기

타입스크립트에서 수행되는 안전성 검사가 엄격 모드보다 더 엄격한 체크를 하기 때문에, use strict는 무의미하다.

요약

  • 모던 자바스크립트의 최신 기능들을 적극 사용하자.
  • TS 개발 환경에서 컴파일러와 언어 서비스를 통해 클래스, 구조분해, async/await 같은 기능을 쉽게 배울 수 있다.
  • TC39의 깃헙 저장소와 타입스크립트의 릴리스 노트를 통해 최신 기능을 확인할 수 있다.

타입스크립트 도입 전에 @ts-check와 JSDoc으로 시험해 보기

@ts-check 지시자를 사용하면 타입스크립트 전환시에 어떤 문제가 발생하는지 미리 알 수 있다. 하지만 이 지시자는 매우 느슨한 수준으로 타입 체크를 수행하고, 심지어 noImplicitAny 설정을 해제한 것보다 헐거운 체크를 수행한다.

사용법

// @ts-check
const person = {first: 'Grace', last: 'Hopper'};
person.first * 2 // Error
// 산술 연산 오른쪽은 'any', 'number' 'bigInt' 또는 열거형이여야 합니다.

위처럼 간단 오류 외에도 선언되지 않은 전역 변수, 알 수 없는 라이브러리 등에서 사용이 가능하다.

JSDoc 주석을 이용하면 자바스크립트 상태에서도 타입 단언과 추론이 가능하다.

요약

타입스크립트는 .ts 파일에서 가장 잘 동작하며 마이그레이션의 궁극적인 목표는 모든 코드가 타입스크립트 기반으로 전환되는 것이다.

아이템 60. allowJs로 타입스크립트와 자바스크립트 같이 사용하기

대규모 프로젝트 경우에는 한꺼번에 마이그레이션 하는 것이 불가능하므로 점진적으로 전환할 수 있어야 한다.

그 기간 중에 자바스크립트와 타입스크립트가 동시에 동작할 수 있도록 해야하는데 그 방법의 핵심이 allowJs 컴파일러 옵션이다.

allowJs 옵션은 타입스크립트 파일과 자바스크립트 파일을 서로 임포트 할 수 있게 해준다.

빌드 체인을 직접 구성했다면 복잡한 작업이 필요할텐데 한 가지 방책으로 outDir 옵션을 사용할 수 있다.

outDir 옵션 사용 시 타입스크립트가 outDir에 지정된 디렉토리에 소스 디렉터리와 비슷한 구조로 자바스크립트 코드를 생성하고 지정된 디렉터리를 대상으로 기존 빌드 체인을 실행하면 된다.

요약

  • 점진적 마이그레이션을 위해 allowJs 컴파일러 옵션을 사용하자.
  • 대규모 마이그레이션 작업 시작 전에, 테스트와 빌드 체인에 타입스크립트를 적용해야 한다.

아이템 61. 의존성 관계에 따라 모듈 단위로 전환하기

다른 모듈에 의존하지 않는 최하단 모듈부터 작업을 시작해서 의존성 최상단에 있는 모듈을 마지막으로 완성하자.

서드파티 라이브러리

  • 일반적으로 @types 모듈 설치

외부 API

  • API에 대한 사양을 기반으로 타입 정보 생성

두 개 모두 프로젝트 내의 모듈에 의존하지 않기 때문에 먼저 해결하는 것이 좋다.

마이그레이션할 때는 타입 정보 추가만 하고, 리팩터링을 하지 말자. 당장의 목표는 코드 개선이 아니라 타입스크립트로 전환하는 것이다.

마이그레이션 중 발생할 수 있는 오류는 다음과 같다.

선언되지 않은 클래스 멤버

  • 타입스크립트에서는 클래스 멤버 변수를 명시적으로 선언해야 한다.

타입이 바뀌는 값

const state = {};
state.name = 'New York' //js일 때는 ok
// error '{}' 유형에 'name' 속성이 없습니다.
  • 객체를 한꺼번에 생성하지 않으면 존재하지 않는 속성값으로 오류가 발생할 수 있다.

JSDoc 타입 정보를 타입스크립트 타입으로 전환해 주는 빠른 수정 기능이 있다.

자바스크립트 상태에서 JSDoc과 @ts-check를 사용한 상태라면 타입스크립트로 전환하는 순간 타입 정보가 무효화 된다는 것을 주의하자.

마지막 단계로, 테스트 코드를 타입스크립트로 전환하면 된다. 로직 코드가 테스트 코드에 의존하지 않기 때문에, 항상 의존성 관계도의 최상단에 위치한다.

  • 마이그레이션 중에도 테스트를 수행 가능해진다.

요약

  • 의존성 관계도 아래에서부터 위로 올라가며 마이그레이션 하자.
  • 리팩터링 목록을 만들어만 놓고 타입스크립트 전환에 집중하자.

마이그레이션의 완성을 위해 noImplicitAny 설정하기

noImplicitAny가 설정되지 않은 상태에서는 타입 선언에서 비롯되는 실제 오류가 숨어있기 때문에 마이그레이션이 완료되었다고 할 수 없다.

요약

  • noImplicitAny 설정을 활성화하여 마이그레이션의 마지막 단계를 진행하자. 그래야 타입 선언과 관련된 실제 오류가 보여진다.
  • noImplicitAny를 전면 적용하기 전에 로컬에서부터 타입 오류를 점진적으로 수정하자.

최종적으로 가장 강력한 설정은 strict: true 이다. 타입스크립트에 익숙해진 다음 조금씩 타입 체크 강도를 높여가자.

profile
주니어 프론트엔드 개발자가 되고 싶습니다!

0개의 댓글