Sua Blog

Type Guard

Type Guard

타입 가드(Type Guard)를 사용하면 조건문을 통해 타입을 좁혀 나갈 수 있다.

unknown

unknown 타입은 any 타입과 동일하게 모든 값을 허용하지만, 할당된 값이 어떤 타입인지 모르게 때문에 함부로 프로퍼티나 연산을 할 수 없다.

unknown 타입으로 변수를 정의하면 컴파일러에게 변수의 타입이 unknown이라 어떤 값이든 올 수 있으니 엄격하게 검사해라라고 요청하는 것과 동일하다.

unknown 타입의 변수를 사용할 때는 타입 가드를 해야하고 문제되는 코드를 미리 예방할 수 있다.

const whatType = (value: unknown): string => {
  if (typeof value === 'string') {
    return 'string';
  } else if (typeof value === 'number') {
    return 'number';
  } else if (typeof value === 'boolean') {
    return 'boolean';
  } else if (typeof value === 'object') {
    return 'object';
  } else if (typeof value === 'function') {
    return 'function';
  }
  return 'undefined';
};

typeof

TypeScript는 JavaScript의 instanceof, typeof 연산자를 이해할 수 있다. 즉 조건문에 typeof와 instanceof를 사용하면, TypeScript는 해당 조건문 블록 내에서는 해당 변수의 타입이 다르다는 것(=좁혀진 범위의 타입)으로 이해한다.

typeof type guard에서 타입 하나를 리턴할 경우 해당 타입으로는 더이상 추론되지 않는다.

const getNumber = (value: number | string): number => {
  if (typeof value === 'number') {
    return value;
  }
  return -1;
};

in

in은 객체 내부에 특정 property가 존재하는지를 확인하는 연산자로 type guard로 활용할 수 있다.

interface Admin {
  id: string;
  role: string;
}

interface User {
  id: string;
  email: string;
}

const redirect = (user: Admin | User) => {
  if ('role' in User) {
    console.log('Admin'); // Admin
  } else {
    console.log('User'); // User
  }
};

리터럴

리터럴 타입은 동등(==), 일치(===)연산자 또는 switch로 타입 가드를 할 수 있다.

type TriState = 'yes' | 'no' | 'unknown';

const logOutState = (state: TriState) => {
  if (state === 'yes') {
    console.log('사용자가 yes를 골랐습니다.');
  } else if (state === 'no') {
    console.log('사용자가 no를 골랐습니다.');
  } else {
    console.log('사용자가 아직 결정을 내리지 않았습니다.');
  }
};

union 타입에 리터럴이 있는 경우에도 공통 property 값을 비교해 구분할 수 있다.

type Drink = {
  kind: 'drink', // 리터럴 타입
  drink: string,
};

type Food = {
  kind: 'food', // 리터럴 타입
  food: string,
};

const somethingEat = (arg: Drink | Food) => {
  if (arg.kind === 'drink') {
    console.log(arg.drink); // 성공
    console.log(arg.food); // 에러 발생
  } else {
    console.log(arg.drink); // 에러 발생
    console.log(arg.food); // 성공
  }
};

null과 undefined (strictNullCheks)

const foo = (a: number | null) => {
  if (a === null) return;
  // 이제부터 a는 무조건 number이다.
};

Custom Type Guard

사용자 정의 타입 가드는 단순히 어떤 인자명은 어떠한 타입이다라는 값을 리턴하는 함수다.

type Car = {
  type: 'CAR';
  wheel: number;
};

type Boat = {
  type: 'BOAT';
  motor: number;
};

  const getWhellOrMotor = (machine: any): number => {
    if (isCar(machine)) {
      return machine.wheel; // machine은 Car
    } else if (isBoat(machine)) {
      return machine.motor; // machine은 Boat
    } else {
      return -1;
    }
  };

    const isCar = (arg: any): arg is Car => {
    return arg.type === 'CAR';
  };

  const isBoat = (arg: any): arg is Boat => {
    return arg.type === 'BOAT';
  };

is keyword를 사용하면 isCar 함수가 true를 반환하면 Typescript는 형식을 Car로 좁힌다.

유형 술어 https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards

참고

https://radlohead.gitbook.io/typescript-deep-dive/type-system/typeguard