타입 좁히기(Type Narrowing)
는 예전에 unknown 타입
을 다루면서 언급만 했었는데 타입 좁히기가 무엇이고 어떤 일을 하는지 이번 포스트에서 알아보도록 하겠습니다.
아래와 같은 함수가 존재할 때, 타입스크립트는 변수 x
가 number
인지 string
인지 확실하게 알 수 없으므로 에러를 발생시킵니다.
const foo = (x: number | string): void => {
x.toString(2); //Error!!
x.split(''); //Error!!
};
이런 상황에서 각 상황에 맞는 메소드를 사용하고 싶다면 다음과 같이 코드를 수정해야합니다.
const foo = (x: number | string): void => {
if (typeof x === 'number') {
x.toString(2);
}
if (typeof x === 'string') {
x.split('');
}
};
if
구문을 통해 x
의 타입을 보장하여 메소드 사용과정에서 발생하는 오류를 없앨 수 있습니다. 이때 첫 번째 조건문에서는 x가 number 타입
으로 보장되고, 두 번째 조건문에서는 x가 string
타입으로 보장됩니다.
이렇게 타입이 보장되면 그 이후로는 보장된 타입으로 자동적으로 변경되며 이것을 타입 좁히기(Type Narrowing)
라고 합니다.
타입스크립트는
else
문도 대응하므로 if 문 하나를 통해 타입 하나를 좁히면 else 문에서는 if 문으로 좁힌 타입이 될 수 없게됩니다.const foo = (x: number | string): void => { if (typeof x === 'number') { x.toString(2); } else { //if문에서 number가 아니었으므로 당연히 x는 string 타입이 된다고 타입을 좁힌다. x.split(''); } };
if (typeof x === 'number')
위의 코드 처럼 조건문 if
와 typeof
를 통해서 타입 좁히기를 하게 만들어주는 표현식을 타입 가드(Type Guard)
라고 부릅니다.
타입 가드는 typeof
말고도 여러가지 표현식을 사용해서 만들 수 있습니다. 타입 가드를 수행하는 대상이 객체인지 변수인지 등에 따라서 사용하는 연산자만 다르고 사용방법은 비슷합니다.
typeof
는 예제 코드에서 봐았던 방식으로 타입 가드를 만들어줍니다. if
와 같은 조건문과 함께 사용해 타입 가드를 만들 수 있습니다.
if (typeof x === 'number') {}
if (typeof x === 'string') {}
instanceof
는 A instanceof B
에서 A 객체가 B 클래스의 인스턴스인지를 판별하는 연산자입니다. 간단히 말해 A가 B에 속하는지를 판별해주는 연산자라고 생각하면 됩니다.
class A {
x = 'hello';
}
class B {
y = 'hi';
}
const foo = (arg: A | B) => {
if (arg instanceof A) {
console.log(arg.x);
}
if (arg instanceof B) {
console.log(arg.y);
};
in
연산자는 객체에 프로퍼티가 존재하는지 확인합니다. 객체의 프로퍼티를 가지고 타입 좁히기를 하고싶은 경우 사용합니다.
type A = {
x: string;
};
type B = {
y: string;
};
const foo = (arg: A | B) => {
if ('x' in arg) {
//arg에 x라는 프로퍼티가 존재하면 param A 객체로 타입 좁히기
}
};
리터럴 값은 비교 연산자 ===, ==, !==, !=
를 사용하여 타입 가드를 만들 수 있습니다.
type X = 0 | 1;
const foo = (arg: X) => {
if (arg === 0) {
}
else if (arg === 1) {
}
};