문제의 역사: 타입스크립트는 일반적인 타입 체크를 확장하여 개발자가 객체가 특정 타입에 부합하는지를 검사하는 사용자 정의 함수인 타입 가드를 만들 수 있게 합니다. 이는 유니언 타입, 동적 구조 및 값의 타입이 다를 수 있는 API와 작업하는 데 필요합니다.
문제는 종종 typeof 및 instanceof를 통한 일반적인 타입 체크가 원시 타입과 클래스에 제한되며, 하지만 구조체나 복잡한 타입은 그렇게 정의할 수 없다는 점입니다. 컴파일러에게 값이 안전하게 원하는 타입으로 좁혀지는 경우를 명시적으로 알려주는 방법이 필요합니다.
해결책은 다음과 같은 형태의 타입 가드 함수를 작성하는 것입니다: function isCat(obj: Animal): obj is Cat {...}, 여기서 중요한 점은 함수의 반환 타입에 타입 프레디케이트가 있다는 것입니다.
코드 예시:
interface Dog { bark: () => void; } interface Cat { meow: () => void; } type Pet = Dog | Cat; function isDog(pet: Pet): pet is Dog { return (pet as Dog).bark !== undefined; } function makeSound(pet: Pet) { if (isDog(pet)) { pet.bark(); } else { pet.meow(); } }
주요 특징:
타입 가드 함수에서 true/false를 반환하기만 하면 컴파일러가 타입을 좁혀주나요?
아니요. 반환 타입을 타입 프레디케이트(예: pet is Dog)로 명시적으로 지정하는 것이 중요합니다. 그렇지 않으면 타입스크립트는 값의 타입을 자동으로 좁히지 않습니다. 함수가 단지 true 또는 false를 반환하더라도요.
타입 가드를 콜백 내에서 사용할 수 있나요 (예: filter에서 사용)? 그리고 축소가 올바르게 작동하나요?
네, 타입 가드가 올바르게 주석 처리되어 있다면, 컴파일러는 filter 후 배열 요소의 타입을 좁히고 forEach/callback 함수 내에서도 마찬가지입니다. 하지만 주석이 누락되거나 잘못 작성되면 결과는 특정 타입 대신 유니언 타입을 갖게 됩니다.
const pets: Pet[] = [...]; const dogs = pets.filter(isDog); // 타입스크립트는 dogs: Dog[]임을 알 수 있습니다.
사용자 정의 타입 가드가 typeof, instanceof를 통한 일반적인 타입 체크와 어떤 점에서 다르나요?
타입 가드 함수는 모든 구조체에 대한 검사를 구현하고, 복잡한 수준의 검사를 설명하고, 클래스 없이 인터페이스를 조작할 수 있으며, 기본 타입과 클래스만을 사용하는 것이 아닙니다.
함수가 타입 프레디케이트 없이 타입 가드를 사용하여 사용자를 필터링합니다:
function isValidAdmin(user: any): boolean { return user.isAdmin === true; } const admins = users.filter(isValidAdmin); // admins: any[]
장점:
단점:
함수가 올바른 타입 프레디케이트로 필터링을 수행합니다:
interface Admin { name: string; isAdmin: true; } function isAdmin(user: any): user is Admin { return user && user.isAdmin === true; } const admins = users.filter(isAdmin); // admins: Admin[]
장점:
단점: