타입 좁히기

Raccoon·2025년 5월 21일

본 포스팅은 한 입 크기로 잘라먹는 타입스크립트 강의를 참고하여 작성했습니다. 문제가 될 소지가 있다면 댓글로 알려주세요. 바로 수정하겠습니다.

타입 좁히기

타입 좁히기 란 조건문 등의 분기 로직을 통해 복수의 타입이 가능한 값을 구체적인 단일 타입으로 좁히는 것을 말한다.

언제 필요한가?

함수의 매개변수가 union 타입인데, 분기별로 매개변수의 메서드를 사용해야 한다거나, 매개변수의 속성 값을 사용할 때와 같은 상황에 필요하다.

function func(value: number | string) {
  value.toFixed(); // error!!
}

위와 같은 상황에서, value가 어떤 타입인지 정확히 좁혀지지 않았기 때문에, number 타입 메서드인 toFixed() 를 사용하면 에러가 나는 것이다.

어떻게 사용하나?

타입스크립트가 변수의 타입을 좁히도록 도와주는 도구를 타입 가드 라고 한다.
타입 가드 를 적절히 사용하여 타입을 좁힐 수 있다.

typeof

typeof 를 통해 해당 타입이 어떤 타입인지 알아낼 수 있다.

function func(value: number | string) {
	if(typeof value === "number") {
      console.log(value.toFixed());
      // value : number
    } else if(typeof value === "string") {
      console.log(value.toUpperCase());
      // value : string
    }
}

이렇게 if 문과 typeof 를 이용해 분기를 나눈다면, 해당 블록 내에서 각각 좁혀진 타입으로 인식하게 된다.
이렇게 되면 각 타입의 메서드를 사용해도 문제가 없다.

본문과는 관계 없는 이야기지만, 타입스크립트에서는 typeof를 타입 정의에 사용하면 기존과 다른 동작을 수행한다.

const person = {
  name: "홍길동",
  age: 26,
};

type Person = typeof Person;

// 다음처럼 타입이 추출된다.
// type Person = {
//    name: string;
//    age: number;
// }

이렇게 사용하면, person 객체의 타입을 뽑아내서 새로운 타입으로 정의할 수 있다.

instanceof

instanceof 는 왼쪽 값이 오른쪽 클래스(생성자 함수)의 인스턴스인지 확인하고 맞다면 true를, 아니라면 false를 반환한다.

type Person = {
  name: string;
  age: number;
};

function func(value: Date | null | Person) {
	if(value instanceof Date) {
      // value : Date
    } //else if(value instanceof Person) {
      // error!!
    //}
}

여기서 주의할 점은, 사용자 정의 타입인 Person은 클래스가 아닌 타입이기 때문에, 주석 처리된 부분처럼 사용할 수 없다는 점이다.
Date 는 JS 내장 클래스이기 때문에 가능하다.

그렇다면, 사용자 정의 타입은 어떤 식으로 확인할 수 있을까?

in

in 키워드는 지정한 속성의 이름이 객체 자체 또는 그 객체의 프로토타입 체인에 존재하면 true를, 그렇지 않다면 false를 반환하게 한다.

type Person = {
  name: string;
  age: number;
};

function func(value: number | Person) {
  if (typeof value === "number") {
    value.toFixed();
  } 
  else if ("age" in value) {
    // value : Person
  }
}

age 속성이 Person 타입에 존재하기 때문에, 타입이 Person 으로 좁혀진다.

하지만, 실제로 코드를 처음 본 상황이라면 저 분기의 value의 타입이 Person 임을 단번에 인지하기란 쉽지 않다.

in을 사용하면 직관성이 떨어지는데, 이를 해결할 방법으로 서로소 유니온 타입사용자 정의 타입 가드 가 해결책이 될 수 있다.

서로소 유니온 타입

서로소 유니온 타입 이란, 교집합이 없는 타입들로만 만든 유니온 타입을 말한다.

앞서 살펴본 in 키워드를 통해 타입 좁히기를 진행한 예시를 보자.

type Admin = {
  name: string;
  kickCount: number;
};
type Member = {
  name: string;
  point: number;
};
type Guest = {
  name: string;
  visitCount: number;
};

type User = Admin | Member | Guest;

function login(user: User) {
  if ("kickCount" in user) {
    // user : Admin
  } else if ("point" in user) {
    // user : Member
  } else if ("visitCount" in user) {
    // user : Guest
  }
}

login 기능을 Admin Member Guest 타입에 따라 설정할 수 있도록 만들어두었다.
하지만, 직관성이 떨어져 user 의 타입을 추론하는데 시간이 걸린다.

다음은 서로소 유니온 타입을 사용한 예시이다.

type Admin = {
  tag: "ADMIN";
  name: string;
  kickCount: number;
};

type Member = {
  tag: "MEMBER";
  name: string;
  point: number;
};

type Guest = {
  tag: "GUEST";
  name: string;
  visitCount: number;
};

type User = Admin | Member | Guest;

function login(user: User) {
  switch (user.tag) {
    case "ADMIN": {
      // user : Admin
    }
    case "MEMBER": {
      // user : Member
    }
    case "GUEST": {
      // user : Guest
    }
  }
}

tag 라는 속성이 각 타입에 추가되었는데, 추가된 tag의 타입은 "ADMIN" "MEMBER" "GUEST" 로, 문자열 리터럴 타입이다.

세 타입에 각기 다른 타입이 추가되어, User 는 서로소 유니온 타입이 되었고 이를 이용해 swtich 문으로 직관성을 향상시키면서 타입 좁히기를 할 수 있게 되었다.

그런데 만약, 서로소 유니온 타입을 만들 수 없는 상황에서 타입 좁히기를 해야한다면, 사용자 정의 타입 가드 를 사용할 수 있다.

사용자 정의 타입 가드

type Dog =  {
  name: string;
  isBark: boolean;
}

type Cat = {
  name: string;
  isScratch: boolean;
}

type Animal = Dog | Cat;

function isDog(animal: Animal) : animal is Dog { // 함수가 참이면, 매개변수가 해당 타입임을 명시한다.
  return (animal as Dog).isBark !== undefined;
}

function warning(animal: Animal) {
  if(isDog(animal)) {
    // animal : Dog
  }
}

isDog 함수는 Animal 타입의 매개변수를 인자로 받는다.
animal is Dog 부분은, 함수가 true를 반환하는 경우, animalDog 타입임을 타입스크립트에게 알려주는 사용자 정의 타입 가드 시그니처 이다.
return 문은 특정 객체에만 존재하는 프로퍼티를 검사하여, true 또는 false 값을 반환하도록 한다.
즉, 타입 단언을 통한 return 문은, 특정 타입에만 존재하는 프로퍼티 값을 확인하여 true 혹은 false 를 반환하는 역할을 하고,
is 키워드는 해당 값이 true 라면 해당 변수가 Dog 임을 명시해준다.

profile
꾸준함을 목표로 합니다.

0개의 댓글