타입스크립트

타입스크립트 (4) - 타입 가드란? what is Type Guard in TypeScript?

개형이 2022. 5. 24. 00:09

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

이를 잘 활용하면 가독성과 생산성이 좋은 코드가 될 수 있다고 하니 어떻게 사용하는지 기록해둘 것이다.

 

 


 

typeof 를 사용한 타입 가드


Primitive 타입일 경우 typeof Type Guard을 주로 사용한다. 예시는 다음과 같다.

function getNumber (value: number | string): number {
  value; // number | string
  if (typeof value === "number") {
	value; // number
	return value; 
	// typeof type guard에서 타입 하나를 리턴할 경우 해당 타입으로는 더이상 추론되지 않습니다.
  }
  value; // string
  return -1;
}

 

-> 위 예제를 보면 typeof로 조건문을 걸어주는 것을 통해 타입을 좁혀 나가는 것을 확인할 수 있다 🙌

 

 

instanceof 를 사용한 타입가드


instanceof를 이용한 타입가드는 주로 Error 객체를 구분할 때 많이 쓰인다고 하는데, 예제를 살펴보자.

class NegativeNumberError extends Error {}

function getNumber(value: number): number | NegativeNumberError {
  if (value < 0) return new NegativeNumberError();

  return value;
}

function main() {
  const num = getNumber(-10); // 에러를 반환하지 않습니다.

  if (num instanceof NegativeNumberError) {
    return; // num이 NegativeNumberError의 인스턴스일 경우 return
		// typeof num === "object"이므로 typeof를 쓸 경우 정확한 타입가드를 할 수 없음
  }

  num; // number
}

-> 위 예제를 보면 첫번째 예제에서 본 typeof로 타입을 좁혀 나가는 방식과 유사한 것 같지만 instanceof를 사용했다.

     이유는 주석을 보자!  🙀

 

 

in 을 사용한 타입가드


in을 객체의 프로퍼티 유무로 조건문을 사용할 수 있다. 예제를 살펴보자.

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

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

function redirect(user: Admin | User) {
  if("role" in user) {
    routeToAdminPage(user.role); // Admin
  } else {
    routeToHomePage(user.email); // User
  }
}

-> 프로퍼티 유무를 통해 타입을 좁혀나가는 것을 확인할 수 있다.

 

 

literal type의 타입가드


리터럴 값의 경우 === / == / !== / != 연산자를 사용해 타입을 구분할 수 있다. 예제를 살펴보자.

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

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

-> 위 코드는 이해하기가 쉽다 😃

 

유니온 타입에 리터럴 타입이 있는 경우도 있는데 동일하게 적용하면 된다. 예제를 보자.

type Foo = {
  kind: 'foo', // 리터럴 타입
  foo: number
}
type Bar = {
  kind: 'bar', // 리터럴 타입
  bar: number
}

function doStuff(arg: Foo | Bar) {
  if (arg.kind === 'foo') {
    console.log(arg.foo); // 성공
    console.log(arg.bar); // 에러 발생
  }
  else {
    console.log(arg.foo); // 에러 발생
    console.log(arg.bar); // 성공
  }
}

-> 위 코드의 경우 Foo, Bar 타입에 각각의 literal type의 property가 있어 이를 조건문으로 비교하여 타입가드한 것을 확인할 수 있다.

 

 

Custom으로 만드는 타입가드


Type Guard를 커스텀하게 작성하여 적용할 수도 있다. 예제는 다음과 같다.

function getWhellOrMotor(machine: any): number {
  if (isCar(machine)) {
    return machine.wheel; // machine은 Car 타입이 된다
  } else if (isBoat(machine)) {
    return machine.motor; // machine은 Boat 타입이 된다
  } else {
    return -1;
  }
}

// Custom Type Guard
function isCar(arg: any): arg is Car { 
    return arg.type === 'CAR';
}

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

 

-> arg is Car : 함수의 반환값이 true일 경우 arg의 타입이 Car로 적용되는 것을 의미한다.

 


 

이번 포스팅에는 타입 가드를 간략하게 정리해두었다.

다음 포스팅에는 tsconfig에는 어떤 옵션을 적용할 수 있는지 기록해 둘것이다.

 

재밌는 타입스크립트...😇