타입스크립트의 컴파일러는 어떤 역할을 할까?

이 둘이 완전히 독립적이라는 것이 중요하다.

타입스크립트가 할 수 있는 일과 할 수 없는 일이 무엇인지 대충 짐작이 가능하다.

타입 오류가 있는 코드도 컴파일이 가능

// test.ts
let x = 'hello';
x = 1234;

// tsc test.ts를 통해 컴파일 => 결과 test.js
let x = 'hello';
x = 1234;        // 실행 시 제대로 작동함

C나 자바는 타입 체크와 컴파일이 동시에 이루어지나, 타입스크립트는 그렇지 않다. 타입스크립트의 오류는 경고와 같아서 문제 여부를 알려주지만 빌드를 멈추지는 않는다.

코드에 오류가 있다고 하더라도 컴파일된 어플리케이션은 동작하므로 실제로는 개발자들에게 도움이 된다.

런타임에는 타입 체크가 불가능

interface Square {
  width: number
}

interface Rectangle extends Square {
  height: number
}

type Shape = Square | Rectangle

function calculateArea(shape: Shape) {
  if (shape instanceof Rectangle) {
    // 'Rectangle' only refers to a type, but is being used as a value here.
    return shape.width * shape.height
    // Property 'height' does not exist on type 'Shape'.
  } else {
    return shape.width * shape.width
  }
}

instanceof 체크는 런타임에 일어나는 일이다. 하지만 Rectangle은 타입이다. 런타임에는 타입 체크가 일어나지 않으므로 런타임에서 Rectangle은 아무런 역할도 할 수 없다. 실제로 자바스크립트로 컴파일되는 과정에서 인터페이스, 타입, 타입 구문은 모두 제거되어 버린다.

런타임에 타입 정보를 유지할 수 있는 방법은 없을까?

일단 height 속성이 존재하는지 확인하는 방법이 있다.

function calculateArea(shape: Shape) {
  if ('height' in shape) {
    return shape.width * shape.height
  } else {
    return shape.width * shape.width
  }
}

또는 태그 기법을 통해 런타임에 접근 가능한 타입 정보를 명시적으로 저장할 수 있다.