1. 개요

특정 scope 내 변수의 타입을 구체적으로 좁힐 수 있도록 해주는 기법이다.
예를 들어 함수의 인자로 여러 타입의 값이 들어올 수 있을 때, type guard를 이용하면 타입에 따른 적절한 로직을 실행할 수 있다.

const foo = (n: number | string) => {
  if (typeof n === "number") {
    // number logic
  } else {
    // string logic
  }
};

2. 방법

앞서 코드로 살펴본 typeof 뿐만 아니라 in 혹은 instanceof 키워드를 통해서도 type guard를 할 수 있다.

class Dog {}
class Cat {}
 
const foo = (animal: Dog | Cat) => {
  if (animal instanceof Cat) {
    //
  } else {
    //
  }
};

TypeScript는 결국 JavaScript로 변환되어 실행되기 때문에, type guard를 하기 위한 조건문 내 로직이 TypeScript에서만 존재하는 문법으로 이루어져있다면, JavaScript로 변환할 수 없기 때문에 에러가 발생한다는 점을 알아야 한다.

예를 들어 interface 키워드는 TypeScript에서만 존재하는 개념이기 때문에 JavaScript로 변환할 수 없다.
따라서 interface로 선언된 타입을 조건문 내에서 사용할 수 없다.
(interface가 아니라 type으로 선언되었어도 같은 오류가 발생한다)

interface Cat {}
interface Dog {}
 
const foo = (animal: Cat | Dog) => {
  if (animal instanceof Cat) {  // 'Cat' only refers to a type, but is being used as a value here.
    //
  } else {
    //
  }
};

3. 구별된 유니온 (Discriminated Union)

위와 같은 문제를 해결하기 위한 방법으로 discriminated union을 쓸 수 있다.
각각의 interface를 구분할 수 있는 프로퍼티를 제공함으로써 type guard를 할 수 있다.

interface Cat {
  type: "cat";
}
 
interface Dog {
  type: "dog";
}
 
const foo = (animal: Cat | Dog) => {
  if (animal.type === "cat") {
    //
  } else {
    //
  }
};