타입스크립트를 사용하다보면 어떤 변수, 어떤 반환값이 특정 타입인지 아닌지를 구분하는 경우가 많다. 그래서 타입을 구분하는 기능만 따로 만들어져 있는 것이 있다. 그것이 바로 type predicate이다.
먼저 type predicate가 어떤 것인지 살펴보자. number 타입인지 아닌지를 구분하는 함수를 예로 들어보자.
function isNumber(input: any): input is number {
return typeof isNumber === 'number';
}
type predicate는 반환하려는 타입에 [입력값 is 체크하려는 타입] 형식으로 작성한다.
function isNumberReturnBoolean(input: any): boolean {
return typeof isNumber === 'number';
}
이전의 isNumber 함수는 true, false를 리턴하므로 그냥 반환값을 boolean으로 해도 될 것이다. 하지만 굳이 저렇게 사용하는 이유가 뭔지 살펴보자.
let number: any = 5;
if (isNumberReturnBoolean(number)) {
number;
}
if (isNumber(number)) {
number;
}
any타입으로 선언한 number의 타입은 any다. 만약 이 변수를 isNumberReturnBoolean 함수의 입력값으로 넣고, true일 때의 타입을 살펴보자.
또 다른 예제를 보자.
interface Dog {
name: string;
age: number;
}
interface Cat {
name: string;
breed: string;
}
어떤 동물을 입력받을 건데 이 동물의 타입이 Dog인지 아닌지 판별하는 함수를 만들어보자.
type DogAndCat = Dog | Cat;
function isDog(animal: DogAndCat): animal is Dog {
return (animal as Dog).age !== undefined;
}
Dog인지 Cat인지를 판단하기 위해서는 Dog 타입의 age 속성이 있냐 없냐로 볼 수 있다. 따라서 조건문을 입력받은 값의 타입의 age 프로퍼티가 undefined인지 아닌지로 판단하면 된다.
그런데 중요한 것은 만약 입력 값의 타입이 아직 확신하지 못하기 때문에 animal.age라고 하면 에러가 발생한다. 그래서 일단 as Dog으로 캐스팅을 해주고 비교를 하는 것이다.
const dog: DogAndCat = {
name: '도그',
age: 32,
};
if (isDog(dog)) {
dog; // type: Dog
} else {
dog; // never,
}
else 문에서 dog의 타입이 never가 되는 이유는 dog 변수에서 Cat의 타입을 형성할 수 없기 때문이다.
하지만 다음 예제를 보자.
const dog2: DogAndCat =
Math.random() > 0.5
? {
name: '도그',
age: 32,
}
: {
name: '캣',
breed: '한국',
};
if (isDog(dog2)) {
dog2; // Dog
} else {
dog2; // Cat
}
dog2 변수는 Dog 타입 또는 Cat 타입이 될 수 있다. 그래서 만약 isDog 함수에서 dog2가 true라면 Dog 타입일 것이고, 아니라면 자동적으로 Cat 타입이 되는 것이다.