type Dog = {
name: string;
isBark: boolean;
};
type Cat = {
name: string;
isScratch: boolean;
};
type Animal = Dog | Cat;
function warning(animal: Animal) {
if ("isBark" in animal) {
console.log(animal.isBark ? "짖습니다" : "안짖어요");
} else if ("isScratch" in animal) {
console.log(animal.isScratch ? "할큅니다" : "안할퀴어요");
}
}
2개의 타입 Dog와 Cat의 유니온 타입인 Animal 타입까지 정의했습니다.
매개변수로 Animal 타입의 값을 받아 동물에 따라 각각 다른 경고를 콘솔에 출력하는 함수를 만들었습니다. 이때, Dog또는 Cat타입인지 알기 위해 in 연산자
를 이용해 타입을 좁힙니다.
하지만 Dog타입이나 Cat타입의 프로퍼티가 중간에 수정되거나 추가 또는 삭제될 때 타입가드는 제대로 동작하지 않습니다.
function isDog(animal: Animal): animal is Dog {
return (animal as Dog).isBark !== undefined;
}
function isCat(animal: Animal): animal is Cat {
return (animal as Cat).isScratch !== undefined;
}
function warning(animal: Animal) {
if (isDog(animal)) {
console.log(animal.isBark ? "짖습니다" : "안짖어요");
} else {
console.log(animal.isScratch ? "할큅니다" : "안할퀴어요");
}
isDog
함수의 매개변수로 받은 값이 Dog 타입이라면 true 아니라면 false를 반환합니다. 이때 반환값의 타입으로 animal is Dog
를 정의해 줍니다.
이렇게 하면 함수가 true를 반환하면 조건문 내부에서는 이 값이 Dog타입임을 보장한다는 의미가 됩니다.
type User = {
id: number;
name: string;
email: string;
};
function isUser(data: any): data is User {
return (
typeof data.id === 'number' &&
typeof data.name === 'string' &&
typeof data.email === 'string'
);
}
여기서 isUser는 매개변수로 전달된 data가 User타입인지 검사합니다. 조건이 모두 참이어야 true를 반환하고, 아니면 false를 반환합니다.
반환 타입에서 data is User는 타입스크립트에게 이 값이 User타입이다라고 알리는 역할을 합니다.
const response = { id: 1, name: 'John Doe', email: 'john@example.com' };
if (isUser(response)) {
console.log(response.name);
} else {
console.error('Invalid user data'); // 타입이 User가 아님
}
조건문 안에서 typescript는 response를 자동으로 User타입으로 간주합니다. 타입 가드가 없을 때보다 코드의 안전성이랑 가독성이 올라갑니다.
type Admin = {
id: number;
role: 'admin';
permissions?: string[];
};
function isAdmin(data: any): data is Admin {
return (
typeof data.id === 'number' &&
data.role === 'admin' &&
(data.permissions === undefined || Array.isArray(data.permissions))
);
}
const person = { id: 1, role: 'admin', permissions: ['read', 'write'] };
if (isAdmin(person)) {
console.log(person.permissions);
}
isAdmin
함수에서 permissions는 선택적 속성이므로 두 가지를 검사해야 합니다.
자바스크립트에서 배열은 객체로 취급되기 때문에 typeof
로 배열을 정확히 판별할 수 없습니다. Array.isArray()
는 값이 배열인지 정확하게 검사합니다.
1️⃣ 동적 데이터 타입
타입 스크립트는 정적 타입검사(컴파일 타임)를 통해 오류를 방지하려고 하지만, 동적 데이터에서는 타입 보장이 어렵습니다.
function processFormData(data: FormData) {
console.log(data.username.toUpperCase());
}
const formData = { username: 'JohnDoe', age: '30' }; // 잘못된 타입 (age가 문자열)
processFormData(formData); // 컴파일 시 오류 없음 -> 런타임에 문제 발생 가능
2️⃣ 명시적으로 타입을 검사
타입스크립트는 타입을 명시적으로 검사하는 코드가 없으면 자동으로 타입을 추론할 수 없습니다.
const formData = { username: 'JohnDoe', age: '30' };
if (typeof formData.age === 'number') {
console.log(formData.age.toFixed(2)); // 타입이 맞는 경우에만 안전하게 동작
} else {
console.log('Invalid data');
}
3️⃣ 불필요하게 타입 검사 반복
타입 가드를 사용하지 않으면 타입 검사를 반복적으로 수행해야 합니다.
const formData = { username: 'JohnDoe', age: '30' };
// 여러 곳에서 타입을 수동으로 검사
if (typeof formData.username === 'string' && typeof formData.age === 'number') {
console.log(formData.username.toUpperCase());
}
if (typeof formData.age === 'number') {
console.log(formData.age.toFixed(2));
}
타입 가드를 사용하면 한 번의 타입 검사를 통해 타입을 안전하게 사용할 수 있습니다. 또한, 여러 곳에서 타입 검사를 하지 않고 타입 가드 함수를 재사용할 수 있습니다.
type FormData = {
username: string;
age: number;
};
function isValidFormData(data: any): data is FormData {
return typeof data.username === 'string' && typeof data.age === 'number';
}
const formData = { username: 'JohnDoe', age: 30 };
if (isValidFormData(formData)) {
console.log('Valid form data:', formData);
} else {
console.error('Invalid form data');
}