Effective TypeScript (item 1-5)

김기환·2022년 6월 30일
0

Effective TypeScript

목록 보기
1/1

아이템 1: 타입스크립트와 자바스크립트의 관계 이해하기

  • 타입스크립트는 자바스크립트의 상위집합(superset)이다.

    • 모든 자바스크립트 프로그램은 타입스크립트 프로그램이다.
    • 그러나 일부 자바스크립트 프로그램만이 타입 체크를 통과한다.
  • 타입 시스템은 런타임 오류를 일으키는 코드를 미리 찾고자 한다.

const states = [
  { name: 'Alabama', capital: 'Montogmery' },
  { name: 'Alaska', capital: 'Juneau' },
  { name: 'Arizona', capital: 'Phoenix' },
  // ...
];
for (const state of states) {
  console.log(state.capitol); // JS에서는 undefined, TS에서는 에러
}
  • 타입을 명시하면 오류를 구체적으로 알 수 있다.
interface State {
  name: string;
  capital: string;
}
const states: State[] = [
  { name: 'Alabama', capitol: 'Montogery' },
  { name: 'Alaska', capitol: 'Juneau' },
  { name: 'Arizona', capitol: 'Phoenix' },
];
// 🚨 Error
// 'State' 형식에 'capitol'이 없습니다.
// 'capital'을(를) 쓰려고 했습니까? 
for (const state of states) {
  console.log(state.capital);
}

아이템 2: 타입스크립트 설정 이해하기

타입스크립트 설정 파일인 tsconfig.json에서 100여가지의 타입 체커 설정을 할 수 있다.

아래의 코드는 타입스크립트 설정에 따라서 타입 체커를 통과할 수도, 통과하지 못할 수도 있다.

 function add(a, b) { // 다음과 같은 타입으로 추론된다: function add(a: any, b: any): any 
   return a + b;
 }
add(10, null);

타입스크립트 설정 중 noImplicitAny이란 설정이 있다. 이 설정은 타입이 명시적으로 선언되지 않았는데 추론된 타입이 any이면 오류를 일으킨다. 따라서 이 설정이 켜져있다면, 타입스크립트는 위의 add 함수에 오류를 일으킨다.

strictNullChecks이라는 설정은 모든 타입에 기본으로 포함되어 있는 null과 undefined를 제거해준다.

// strictNullChecks 해제 시
const x: number = null; // 정상

// strictNullChecks 설정 시
const x: number = null; // 🚨 에러: 'null' 형식은 'number' 형식에 할당할 수 없습니다.

다른 사람들이 작성해둔 타입스크립트 프로젝트 설정을 별 생각없이 따라하기 쉽다. 그러다 보면, 각 설정들이 정확히 어떤 것인지도 파악하기 어렵고, 어떤 설정들을 활용할 수 있는지도 자세히 알지 못한채 지나치기 쉽다. 모든 설정을 다 알 수야 없겠지만, 여러 프로젝트들의 tsconfig.json을 보면서 자주 보이는 설정들은 자세히 알아두어야겠다.

아이템 3: 코드 생성과 타입이 관계없음을 이해하기

  • 타입스크립트 컴파일러는 두 가지 역할을 수행한다.
    • 최신 타입스크립트와 자바스크립트를 구버전의 자바스크립트로 트랜스파일 한다.
    • 코드의 타입 오류를 체크한다.

이 두 가지는 서로 독립적이다. 타입 오류가 있더라도 트랜스파일은 가능하다. 즉, 타입 오류가 있어도 실행이 가능하다.

  • 런타임에는 타입 체크가 불가능하다.

    • 자바스크립트로 트랜스파일되는 과정에서 모든 인터페이스, 타입, 타입 구문은 그냥 제거되어 버린다.

    • 런타임에 타입 정보를 유지하는 방법

      • 특정 속성이 존재하는지 체크
      • 타입을 클래스로 만들기: 타입과 값으로 모두 사용할 수 있다
      • ‘태그’ 기법 - 런타임에 접근 가능한 타입 정보를 명시적으로 저장
      interface Squrre { 
         kind: 'square';
         width: number;
       }
       interface Rectangle {
         kind: 'rectangle';
         height: number;
         width: number;
       }
       type Shape = Square | Rectangle; // '태그된 유니온(tagged union)'
      
       function calculateArea(shape: Shape) {
         if (shape.kind === 'rectangle') {
           shape; // 타입이 Rectangle
           return shape.width * shape.height;
         } else {
           shape; // 타입이 Square
           return shape.width * shape.width;
         }
       }
  • 타입 연산은 런타임에 영향을 주지 않는다.

  • 런타임 오버헤드가 없는데신 타입스크립트 컴파일러는 빌드타임 오버헤드가 있다.

아이템 4: 구조적 타이핑에 익숙해지기

🐤 구조적 타이핑 (덕 타이핑, duck typing)
객체가 어떤 타입에 부합하는 변수와 메서드를 가질 경우 객체를 해당 타입에 속하는 것으로 간주하는 방식

자바스크립트는 본질적으로 덕 타이핑(duck typing) 기반이다.

다음의 예제에서 coord는 Vector3D의 속성을 가지고 있음에도 calculateLength 의 파라미터로 넣을 수 있다.

interface Vector2D {
  x: number;
  y: number;
}

function calculateLength(v: Vector2D) {
  return Math.sqrt(v.x * v.x + v.y * v.y);
}


interface VectorAndCity {
  x: number;
  y: number;
  city: string;
}

const coord = { x: 1, y:2, city: 'seoul'}
calculateLength(coord) // 정상 

타입스크립트는 매개변수 v를 구조적 타이핑 관점으로 다룬다. 즉 coord가 x와 y 속성을 갖기 때문에 calculateLength로 호출이 가능하다.

구조적 타이핑이 문제가 되는 경우가 있다.

function getVector3D (v: Vector3D) {
  for( const axis of Object.keys(v)) {
    const value = v[axis];
                // 에러: 'string'은 'Vector3D'의 인덱스로 사용할 수 없기에
                // 엘리먼트는 암시적으로 'any' 타입입니다.
  }
}

매개변수 v 는 구조적 타이핑에서 열려있기 때문에 다음과 같은 변수가 삽입될 수도 있다.
{ x: 3, y: 5, z:4, address: "Seoul"}
추가적인 키 값에 어떤 타입이 매핑될지를 알 수 없기에 타입스크립트는 v[axis] 가 어떤 타입으로 될지 모르는 것이다.

아이템 5: any 타입 지양하기

타입스크립트의 타입 시스템은 any 타입이나 타입 단언문 as any로 무력화시킬 수 있다.
따라서 any는 타입스크립트의 수많은 장점을 누리지 못하게 막는다.

  • any 타입에는 타입 안정성이 없다

    let age: number;
    age = '12' as any; // 정상
    age += 1; // 런타임에 정상, 🚨 age는 '121'
  • any는 함수 시그니처를 무시해 버린다

    function calculateAge(birthDate: Date): number {
      // ...
    }
    let birthDate: any = '1997-09-12';
    calculateAge(birthDate); // 정상 (🚨 추후 에러 발생 가능)
  • any 타입에는 언어 서비스가 적용되지 않는다

    • IDE의 자동완성 기능과 적절한 도움말 제공 불가
  • any 타입은 코드 리팩터링 때 버그를 감춘다

    • any가 아닌 구체적인 타입을 사용하여 타입 체커가 오류를 발견하도록 해야 한다
  • any는 타입 설계를 감춰버린다

    • 애플리케이션 상태 등의 객체 설계 시 any 사용을 지양해야 한다
  • any는 타입시스템의 신뢰도를 떨어뜨린다

    • 사람은 항상 실수를 한다
    • any 타입을 쓰지 않으면 런타임에 발견될 오류를 미리 잡을 수 있고 신뢰도를 높일 수 있다
profile
Front-end dev · Human-Computer Interaction

0개의 댓글