[TS] 타입을 더 자세하게, 타입 가드

chaevivi·2023년 11월 23일
0
post-thumbnail

타입 가드 (Type Guard)



1. 타입 가드란?

타입 가드란 여러 개의 타입으로 지정된 값을 특정 위치에서 원하는 타입으로 지정하는 것을 의미합니다.

예를 들어, string, number, boolean 타입을 가지고 있는 변수를 어떤 지점에서 string 타입만 갖게끔 만드는 것입니다. 다시 말해 타입을 좁히는(Narrowing) 것입니다.


이를 코드로 표현하면 다음과 같습니다.

function onlyString(inputs: string | number | boolean) {
  // 타입 가드
  if (typeof inputs === 'string') {
    console.log(inputs);
  }
}
  • onlyString 함수에 string 타입이 들어올 때만 inputs가 출력됩니다.


2. 타입 가드 방법


2.1. typeof 연산자

typeof 연산자는 코드의 타입을 문자열로 반환합니다.


function printText(text: string | number) {
  text.trim();    // Error
}
  • printText 함수에서 stringnumber 타입을 갖는 인자를 받아와 trim 메서드를 실행하였습니다.
  • 하지만 trim 메서드를 실행하면 에러가 발생합니다.
  • 왜냐하면 trim 메서드는 string 타입에서만 실행되는 메서드이기 때문입니다. 위의 코드에서는 text 변수가 string 타입일 수도 있지만 number 타입일 수도 있기 때문에 에러가 발생합니다.

function printText(text: string | number) {
  if (typeof text === 'string') {
    console.log(text.trim());
  }
  
  if (typeof text === 'number') {
    console.log(text.toFixed(2));
  }
}
  • 위의 문제점을 해결하면 다음과 같습니다.
  • typeof 연산자로 text 변수의 타입이 string일 때와 number일 때로 나누었습니다.
  • 첫 번째 if문에서는 text 변수가 string 타입으로 추론되어 trim 메서드를 사용할 수 있습니다.
  • 두 번째 if문에서는 text 변수가 number 타입으로 추론되어 toFixed 메서드(Number.prototype.toFixed())를 사용할 수 있습니다.

2.2. in 연산자

in 연산자는 객체에 특정 속성이 있으면 true, 특정 속성이 없으면 false를 반환합니다.


type Fish = { swim: () => void };
type Bird = { fly: () => void };

function move(animal: Fish | Bird) {
    return animal.swim();    // Error
}
  • Fish 타입은 swim 속성을 가지고 있고, Bird 타입은 fly 속성을 가지고 있습니다.
  • move 함수는 Fish 타입이나 Bird 타입을 갖는 animal을 인자로 받습니다.
  • 하지만 swim()을 실행하면 에러가 발생합니다.
  • 왜냐하면 swim()Fish 타입만 갖고 있는 속성인데, animalFish 타입일 수도 있지만 Bird 타입일 수도 있기 때문입니다.

type Fish = { swim: () => void };
type Bird = { fly: () => void };

function move(animal: Fish | Bird) {
  if ("swim" in animal) {
    return animal.swim();
  }
  return animal.fly();
}
  • 위의 문제점을 해결하면 다음과 같습니다.
  • in 연산자로 animalswim 속성이 있는지 확인하고 있으면(true면) swim()을 실행하고, 없으면 fly()를 실행합니다.

2.3. instanceof 연산자

instanceof 연산자는 변수가 대상 객체의 프로토타입 체인에 포함되면 true, 포함되지 않으면 false를 반환합니다.
예를 들어, x instanceof Foox의 프로토타입 체인에 Foo.prototype이 포함되어 있는지 확인합니다.


function logValue(x: Date | string) {
	console.log(x.toUTCString());    // Error
}
  • logValue 함수는 Date 타입이나 string 타입을 갖는 x를 인자로 받습니다.
  • 하지만 x.toUTCString()을 실행하면 에러가 발생합니다.
  • 왜냐하면 toUTCString()Date 타입만 갖고 있는 속성인데, xDate 타입일 수도 있지만 string 타입일 수도 있기 때문입니다.

function logValue(x: Date | string) {
  if (x instanceof Date) {
    console.log(x.toUTCString());
  }
  else {
    console.log(x.toUpperCase());
  }
}
  • 위의 문제점을 해결하면 다음과 같습니다.
  • instanceof 연산자로 xDate의 프로토타입 체인에 포함되면 xDate 타입으로 추론되어 x.toUTCString()가 실행되고, 포함되지 않으면 xstring 타입으로 추론되어 toUpperCase()가 실행됩니다.

2.4. 동등성

타입스크립트에서는 ===, !==, ==, != 으로 동등성을 체크하여 타입 가드를 하기도 합니다.


function example(x: string | number, y: string | boolean) {
  if (x === y) {
    x.toUpperCase();
    y.toLowerCase();
  } else {
    console.log(x);
    console.log(y);
  }
  • example 함수는 string 타입이나 number 타입을 갖는 xstring 타입이나 boolean 타입을 갖는 y를 인자로 받습니다.
  • if 문에서 xy가 같다면 둘의 타입도 같기 때문에 if문이 true이면 xystring 타입이 됩니다.
  • if문이 false이면 xstring이나 number 타입이 될 것이고, ystring이나 boolean 타입이 됩니다.


3. 구별된 유니언 타입

구별된 유니언 타입(discriminated uinons)이란 유니언 타입을 구성하는 여러 개의 타입을 특정 속성 값으로 구분하는 타입 가드 문법을 의미합니다.


interface Haikyuu {
  name: string;
  year: number;
  genre: 'sports';
}

interface Titan {
  name: string;
  year: string;
  genre: 'action';
}

function animation(ani: Haikyuu | Titan) {
  if ('year' in ani) {
    console.log(ani);   
  }
}
  • name, year, genre 속성을 가지고 있는 Haikyuu 인터페이스와 Titan 인터페이스가 있습니다.
  • animation 함수는 Haikyuu 인터페이스나 Titan 인터페이스를 타입으로 갖는 ani를 인자로 받습니다.
  • HaikyuuTitan은 속성의 이름이 모두 같기 때문에 in 연산자로 타입 가드를 할 수 없습니다.

function animation(ani: Haikyuu | Titan) {
  if (ani.genre === 'action') {
    console.log(ani);   
  }
}
  • 위의 문제는 구별된 유니언 타입으로 해결할 수 있습니다.
  • 두 타입에 모두 존재하면서 값으로 구분될 수 있는 속성은 genre 입니다.
  • genreaction일 때로 구분하면 aniTitan 타입으로 추론됩니다.



참고
📖 쉽게 시작하는 타입스크립트
🔗https://www.typescriptlang.org/docs/handbook/2/narrowing.html#exhaustiveness-checking

profile
직접 만드는 게 좋은 프론트엔드 개발자

0개의 댓글