Type Narrowing
function sampleFunc(args: number | boolean): void {
console.log(args + 1);
}
'+' 연산자를 'number | boolean' 및 'number' 형식에 적용할 수 없습니다.
sampleFunc
함수의 매개변수로는 number 타입이 올 수도 있고 string 타입이 올 수도 있다.function sampleFunc(args: number | boolean): void {
// if문 내에서는 args가 number 타입임을 컴파일러가 확신할 수 있다.
if (typeof args === "number") {
console.log(args + 1);
} else {
// number이 아니면 boolean임을 추론한다.
console.log(!args);
}
}
JS에 이미 존재하는 타입 검사 연산자
해당 조건문 코드 블록 내의 타입을 조건 분기를 통해 지정할 수 있다.
if문 뿐만 아니라 switch문을 사용한 타입 가드도 가능하다.
JS와의 호환을 위해 typeof로 가드할 수 있는 타입은 JS에서 제공하는 타입으로 제한된다.
string, number, bigint, boolean, symbol, undefined, object, function
TS는 else를 이해하므로 if문으로 타입을 하나씩 좁혀갈 경우 else 문 안의 변수 타입은 절대 동일한 타입일 수 없음을 인지한다.
배열 타입 가드: Array.isArray()
function sampleFunc(args: number | number[]): void {
// 배열인지 검사하기
if (Array.isArray(args)) {
console.log(args.slice(0, 1));
} else {
console.log(args + 1);
}
}
class Dog {
bark = true;
fly = false;
}
class Bird {
fly = true;
chirp = true;
}
function MyPet(args: Dog | Bird) {
if (args instanceof Dog) {
console.log(args.bark);
console.log(args.fly);
} else {
// instanceof Bird로 추론됨
console.log(args.bark); // ERROR!!
console.log(args.fly);
}
console.log(args.fly);
console.log(args.chirp); // ERROR!
}
MyPet(new Dog());
interface Cat {
sleep: boolean;
fly: boolean;
}
interface Bird2 {
fly: boolean;
chirp: boolean;
}
// sleep이라는 문자가 args 객체에서 key 속성으로 쓰였는지 검사
function NewPet(args: Cat | Bird2) {
if ("sleep" in args) {
console.log(args.fly); // fly, sleep 자동완성
} else {
console.log(args.fly); // never 형식에 fly 속성이 없습니다.
}
console.log(args.fly); // fly만 자동완성됨
}
const myCat: Cat = { sleep: true, fly: false };
NewPet(myCat);
리터럴 값의 경우 ===, ==, !==, !=
연산자를 사용해 타입을 구분할 수 있다.
union 타입에 리터럴 타입이 있는 경우 공통 프로퍼티 값을 비교해 union 타입을 구분할 수 있다.
예: key 값은 같고 value 값은 다른 속성명이 있는 경우
type iphone = { type: "apple"; released: number };
type galaxy = { type: "samsung"; liked: number };
function myPhone(args: iphone | galaxy) {
if (args.type === "apple") {
console.log(args.released); // type, released 자동완성
} else {
console.log(args.liked); // type, liked 자동완성
}
}
값이 다른 공통된 속성의 key에 접근해 구분해주는 방법도 존재한다.
특정 속성이 있고 없고를 찾아 in으로 검사하기 번거로울 때 공통 속성명을 주어 객체의 라벨처럼 사용할 수 있다.
함수 로직을 수행하기 전 미리 데이터가 존재하는 값인지 검사할 수 있다.
const fakeFunc = (props: unknown): void => {
if (props === null || props === undefined) return;
// 함수 로직...
};
== null, != null
을 사용하면 null과 undefined는 모두 early return을 통해 걸러지고 a는 number로 추론된다.
function foo(a?: number | null) {
if (a == null) return;
console.log(a + 1); // a는 number로 추론된다.
}
interface yourDog {
bark: number;
fly: boolean;
}
interface yourBird {
chirp: number;
fly: boolean;
}
// 타입 가드 역할을 하는 커스텀 함수
// yourDog 타입인지 확인 하는 역할을 한다. (리턴 타입에 is 키워드를 붙여 사용한다.)
function dogOrBird(a: yourDog | yourBird): a is yourDog {
// 직접 타입 판별 로직 구현 (bark라는 프로퍼티가 있다면)
if ((a as yourDog).bark) {
// bark, fly 자동완성
return false; // 만일 개면 false
} else {
return true; // 만일 새면 true
}
}
function pet(a: yourDog | yourBird) {
if (dogOrBird(a)) {
// 개일 경우
console.log(a.bark); // bark, fly 자동완성
} else {
// 새일 경우
console.log(a.chirp); // chirp, fly 자동완성
}
}
a is yourDog
부분을 return 값의 타입인 boolean으로 변경하면 에러가 발생한다.a is yourDog
는 사용자 정의 타입 가드를 지정하는 방식이다.a is yourDog
부분을 단순 boolean으로 바꿀 경우 a의 타입이 둘 중 어떤 것인지 파악할 수 없게 된다.a.bark
와 a.chirp
를 사용할 경우 에러가 발생하는 것이다.// 콜백함수 타입가드
declare const FOO: { KEY?: { VALUE: string } };
function IIFE(callbackFunc: () => void) {
callbackFunc();
}
// Type Guard
if (FOO.KEY) {
console.log(FOO.KEY.VALUE);
IIFE(() => {
console.log(FOO.KEY.VALUE); // ERROR! 'FOO.KEY'은(는) 'undefined'일 수 있습니다.
});
}
🤔 왜 발생하는 에러일까?
🧐 어떻게 해결할 수 있을까?
if (FOO.KEY) {
console.log(FOO.KEY.VALUE);
const val = FOO.KEY;
IIFE(() => {
console.log(val.VALUE);
});
}
그 제가 이제 막 배우기시작해서 그런지, 너무 헷갈리고 궁금한 부분이있는데
function dogOrBird(a: yourDog | yourBird): a is yourDog {
// 'bark' 프로퍼티가 있는지 확인
if ((a as yourDog).bark) {
return false; // 'bark'가 있으면 'yourDog'이므로 false 반환
} else {
return true; // 'bark'가 없으면 'yourBird'이므로 true 반환
}
이 부분 제가 이해가 잘 안가는데
코드 대로면
false일때 dog이고
true일떄 bird라는건데
function pet(a: yourDog | yourBird) {
if (dogOrBird(a)) {
// 개일 경우
console.log(a.bark); // bark, fly 자동완성
} else {
// 새일 경우
console.log(a.chirp); // chirp, fly 자동완성
}
}
그러면 이 부분이 반대로 되어야 하는거 아닌가영??