타입 가드(Type Guard)는 TypeScript에서 변수의 타입을 좁히는 데 사용되는 메커니즘입니다. TypeScript는 정적 타입 언어로서 변수의 타입을 컴파일 타임에 결정합니다. 하지만 때로는 런타임에서 변수의 타입이 더 구체적이거나 제한적일 수 있습니다. 이런 경우를 다루기 위해 타입 가드를 사용합니다.
타입 가드를 사용하면 변수의 타입을 더 구체적인 타입으로 좁히거나 확인할 수 있습니다. 이를 통해 타입 시스템이 변수의 타입을 정확히 추론할 수 있게 되며, 코드 안전성과 가독성을 향상시킬 수 있습니다.
가장 일반적인 타입 가드 형태는 typeof
, instanceof
, in
, 그리고 사용자 정의 함수(타입 가드 함수)입니다.
타입단언으로도 타입을 좁힐 수 있지만 실행시점에서의 에러가 발생할 가능성이 있으며 타입단언 문법을 불필요하게 반복적으로 사용하여야 한다는 단점이 있습니다. 따라서 타입단언보다는 타입가드를 사용하는것이 권장됩니다.
타입가드 문법으로는 typeof, instanceof, in 등이 있습니다. 이 문법은 자바스크립트의 문법이며 이를 활용하여 타입 가드를 구현할 수 있습니다.
typeof A: A 타입을 문자열로 반환
A instanceof B: B의 프로토타입 체인에 A가 포함되었는지 boolean 반환 (A가 B의 인스턴스인지 확인)
a in A: A의 속성중에 a가 있는지 boolean 반환
function getNumber(value: string | number): void {
if (typeof value === "string") {
console.log(value.length);
} else {
console.log(value);
}
}
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
class Animal {
type: string;
constructor(type: string) {
this.type = type;
}
}
function printDetails(obj: Person | Animal): void {
if (obj instanceof Person) {
console.log(obj.name); // obj는 Person으로 추론됨
} else {
console.log(obj.type); // obj는 Animal로 추론됨
}
}
만약 아래 코드의 id와 같이 공통된 속성을 in 타입가드로 활용하게 되면 타입가드의 역할을 하지 못한다.
interface Book {
id: number;
rank: number;
}
interface OnlineLecture {
id: string;
url: string;
}
function learnCourse(material: Book | OnlineLecture) : number | string {
if (rank in material) {
return material.rank; // material는 Book 추론됨
} else {
return material.url; // obj는 OnlineLecture로 추론됨
}
if(id in material){
console.log(material.id); //material.id는 number|string으로 추론이 되어, 타입 가드 역할 못함 못하게 된다.(자동완성 기능 사용 불가)
}
}
이 함수는 문법이나 구현된 함수가 아니라 사용자가 직접 타입을 구별하기 위한 함수를 만드는 것을 의미합니다. is 문법을 활용합니다.
두개 interface가 공통으로 id라는 속성을 가지게 된다면 in으로는 구별할 수 없습니다. 따라서 타입 가드 함수를 통해 좀 더 구체적으로 타입을 구별할 수 있습니다.
interface Car {
brand: string;
speed: number;
}
function isCar(vehicle: any): vehicle is Car {
return "brand" in vehicle && "speed" in vehicle;
}
function displayVehicleInfo(vehicle: Car | { type: string }): void {
if (isCar(vehicle)) {
console.log(vehicle.brand, vehicle.speed); // vehicle은 Car로 추론됨
} else {
console.log(vehicle.type); // vehicle은 { type: string }으로 추론됨
}
}
속성의 타입은 타입(number, string...) 뿐만 아니라 실제 값으로도 정의할 수 있습니다. 이럴 경우 속성의 유무가 아닌 실제 값으로 구분을 하여 타입을 구별해야 합니다.
type Shape = "square" | "circle";
interface Square {
kind: "square";
size: number;
}
interface Circle {
kind: "circle";
radius: number;
}
function calculateArea(shape: Shape, data: Square | Circle): number {
if (shape === "square") {
return data.size * data.size;
} else {
return Math.PI * data.radius * data.radius;
}
}