타입 가드란 여러 개의 타입으로 지정된 값을 특정 위치에서 원하는 타입으로 지정하는 것을 의미합니다.
예를 들어, string
, number
, boolean
타입을 가지고 있는 변수를 어떤 지점에서 string
타입만 갖게끔 만드는 것입니다. 다시 말해 타입을 좁히는(Narrowing) 것입니다.
이를 코드로 표현하면 다음과 같습니다.
function onlyString(inputs: string | number | boolean) {
// 타입 가드
if (typeof inputs === 'string') {
console.log(inputs);
}
}
onlyString
함수에 string
타입이 들어올 때만 inputs
가 출력됩니다.typeof
연산자는 코드의 타입을 문자열로 반환합니다.
function printText(text: string | number) {
text.trim(); // Error
}
string
과 number
타입을 갖는 인자를 받아와 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
일 때로 나누었습니다.text
변수가 string
타입으로 추론되어 trim
메서드를 사용할 수 있습니다.text
변수가 number
타입으로 추론되어 toFixed
메서드(Number.prototype.toFixed())를 사용할 수 있습니다.in
연산자는 객체에 특정 속성이 있으면 true
, 특정 속성이 없으면 false
를 반환합니다.
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function move(animal: Fish | Bird) {
return animal.swim(); // Error
}
Fish
타입은 swim
속성을 가지고 있고, Bird
타입은 fly
속성을 가지고 있습니다.Fish
타입이나 Bird
타입을 갖는 animal
을 인자로 받습니다.swim()
을 실행하면 에러가 발생합니다.swim()
은 Fish
타입만 갖고 있는 속성인데, animal
은 Fish
타입일 수도 있지만 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
연산자로 animal
에 swim
속성이 있는지 확인하고 있으면(true면) swim()
을 실행하고, 없으면 fly()
를 실행합니다.instanceof
연산자는 변수가 대상 객체의 프로토타입 체인에 포함되면 true
, 포함되지 않으면 false
를 반환합니다.
예를 들어, x instanceof Foo
는 x
의 프로토타입 체인에 Foo.prototype
이 포함되어 있는지 확인합니다.
function logValue(x: Date | string) {
console.log(x.toUTCString()); // Error
}
Date
타입이나 string
타입을 갖는 x
를 인자로 받습니다.x.toUTCString()
을 실행하면 에러가 발생합니다.toUTCString()
은 Date
타입만 갖고 있는 속성인데, x
는 Date
타입일 수도 있지만 string
타입일 수도 있기 때문입니다.function logValue(x: Date | string) {
if (x instanceof Date) {
console.log(x.toUTCString());
}
else {
console.log(x.toUpperCase());
}
}
instanceof
연산자로 x
가 Date
의 프로토타입 체인에 포함되면 x
는 Date
타입으로 추론되어 x.toUTCString()
가 실행되고, 포함되지 않으면 x
는 string
타입으로 추론되어 toUpperCase()
가 실행됩니다.타입스크립트에서는 ===
, !==
, ==
, !=
으로 동등성을 체크하여 타입 가드를 하기도 합니다.
function example(x: string | number, y: string | boolean) {
if (x === y) {
x.toUpperCase();
y.toLowerCase();
} else {
console.log(x);
console.log(y);
}
string
타입이나 number
타입을 갖는 x
와 string
타입이나 boolean
타입을 갖는 y
를 인자로 받습니다.x
와 y
가 같다면 둘의 타입도 같기 때문에 if문이 true
이면 x
와 y
는 string
타입이 됩니다.false
이면 x
는 string
이나 number
타입이 될 것이고, y
는 string
이나 boolean
타입이 됩니다.구별된 유니언 타입(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
인터페이스가 있습니다.Haikyuu
인터페이스나 Titan
인터페이스를 타입으로 갖는 ani
를 인자로 받습니다.Haikyuu
와 Titan
은 속성의 이름이 모두 같기 때문에 in
연산자로 타입 가드를 할 수 없습니다. function animation(ani: Haikyuu | Titan) {
if (ani.genre === 'action') {
console.log(ani);
}
}
genre
입니다. genre
가 action
일 때로 구분하면 ani
는 Titan
타입으로 추론됩니다.참고
📖 쉽게 시작하는 타입스크립트
🔗https://www.typescriptlang.org/docs/handbook/2/narrowing.html#exhaustiveness-checking