타입 확장의 장점과 더불어 extends, 교차 타입, 유니온 타입간의 차이를 파악하고 언제 사용하면 좋을지 살펴보자.
본문 p.120
타입 확장하기
타입 확장의 장점
유니온 타입 (Union Type)
유니온 타입은 2개 이상의 타입을 조합하여 사용하는 방법이다. 집합의 관점으로 보면 유니온 타입을 합집합으로 해석할 수 있다.
p.122
유니온 타입으로 선언된 값은 유니온 타입에 포함된 모든 타입이 공통으로 갖고있는 속성에만 접근할 수 있다.
예제
interface Human {
name: string;
age: number;
}
interface Humanoid {
id: string;
createdAt: string;
}
function getAge(object: Human | Humanoid) {
return object.age;
}
교차 타입(Intersection Type)
교차 타입은 교집합의 개념과 비슷하다.
p.124
type Intersection = A & B;
예제
interface Human {
name: string;
age: number;
}
interface Humanoid {
id: string;
createdAt: string;
}
type NewWorldPeople = Human & Humanoid;
function getAge(object: NewWorldPeople) {
return object.age;
}
💡궁금하다 궁금해!
책의 본문에는 유니온 타입과 교차 타입에 대해서 이렇게 설명하고 있다.
타입스크립트의 타입을 속성의 집합이 아니라 값의 집합이라고 생각해야 유니온 타입이 합집합이라는 개념을 이해할 수 있다.
p.123
책에서는 속성이 아닌 값의 집합이라고 생각해야한다고 강조한다.(두 번) 하지만 어쨌든 직관적으로 동작하고 있는 모습은 Union 타입은 교집합처럼 동작하고 Intersection 타입은 합집합 처럼 느껴진다.
위에서 들었던 예제들을 보면 합집합이라니까 당연스레 object에 age가 있을 것으로 생각된다. 하지만 그렇지 않다. 도대체 왜이러는 걸까?
stackOverflow에 나와 똑같은 생각을 해서 질문을 올린 사람이 있는데 정말 여러 답변들이 달렸고 내가 제일 도움이 됐던 답변의 링크를 첨부한다.
이 분의 답변은 책에 적혀있는 속성의 집합이 아닌 값의 집합이라고 생각해야한다는 것의 의미를 잘 설명해주고 있다.
위의 Human과 Humanoid를 속성이 아닌 값의 관점에서 Union과 Intersection을 설명할 수 있다.
- Union : Human의 값도 되고 Humanoid의 값도 된다. 그리고 Human과 Humanoid가 합쳐진 NewWorldPeople의 값도 된다.
- Intersection : 오직 NewWorldPeople만 된다.
답변의 예시중 하나를 인용하고자한다.
type A = {
x: number;
y: number;
};
type B = {
y: number;
z: number;
};
type I = A & B;
type U = A | B;
let i: I;
let u: U;
i = { x: 1, y: 2 };
i = { y: 2, z: 3 };
i = { x: 1, y: 2, z: 3 };
u = { x: 1, y: 2 };
u = { y: 2, z: 3 };
u = { x: 1, y: 2, z: 3 };
타입 좁히기 - 타입가드
타입 가드는 크게 자바스크립트 연산자를 사용한 타입 가드와 사용자 정의 타입 가드로 구분할 수 있다.
- 원시 타입을 추론할 때 : typeof 연산자 활용하기
- 인스턴스화된 객체 타입을 판별할 때 : instanceof 연산자 활용하기
- 객체의 속성이 있는지 없는지에 따른 구분 : in 연산자 활용하기
- is 연산자로 사용자 정의 타입 가드 만들어 활용하기
자세한 내용은 책을 참조하기.
타입 좁히기 - 식별할 수 있는 유니온
종종 태그된 유니온으로도 불리우는 식별할 수 있는 유니온은 타입 좁히기에 널리 사용되는 방식이다.
p.139
이 예시는 본문의 예시를 그대로 인용한다. 왜냐하면 우리 코드에도 이와 같은 에러 처리 방식이 똑같이 적용 될 수 있기 때문이다.
type TextError = {
errorCode: string;
errorMessage: string;
};
type ToastError = {
errorCode: string;
errorMessage: string;
toastShowDuration: number;
};
type AlertError = {
errorCode: string;
errorMessage: string;
onConfirm: () => void;
};
type ErrorFeedbackType = TextError | ToastError | AlertError;
const errorArr: ErrorFeedbackType[] = [
{ errorCode: "100", errorMessage: "텍스트 에러" },
{ errorCode: "200", errorMessage: "토스트 에러", toastShowDuration: 3000 },
{ errorCode: "300", errorMessage: "얼럿 에러", onConfirm: () => {} },
{
errorCode: "999",
errorMessage: "잘못된 에러",
onConfirm: () => {},
toastShowDuration: 3000
}
];
타입을 이렇게 거창하게 작성한 이유는 아마도 errorCode 999를 가지는 저 객체에 대해서 에러를 기대했기 때문일 것이다.
그러나 이런 상황에서 자바스크립트는 덕 타이핑 언어이기 때문에 별도의 타입 에러를 뱉지 않는다.
식별할 수 있는 유니온
각 타입이 비슷한 구조를 가지지만 서로 호환되지 않도록 만들어 주기 위해서는 서로 포 함 관계를 가지지 않도록 정의해야한다.
식별할 수 있는 유니온이란 타입 간의 구조 호환을 막기 위해 타입마다 구분할 수 있는 판별자를 달아주어 관계를 제거하는 것이다.
예제
type TextError = {
errorType: "TEXT";
errorCode: string;
errorMessage: string;
};
type ToastError = {
errorType: "TOAST";
errorCode: string;
errorMessage: string;
toastShowDuration: number;
};
type AlertError = {
errorType: "ALERT";
errorCode: string;
errorMessage: string;
onConfirm: () => void;
};
이렇게 수정하면 errorArr에 errorType이 포함되어 자바스크립트가 errorCode 999를 가진 객체에 대해 에러를 반환한다.
{
type:"TEXT",
errorCode: "999",
errorMessage: "잘못된 에러",
onConfirm: () => {},
toastShowDuration: 3000
}
];
판별자 선정
- 리터럴 타입이어야한다.
- 인스턴스화할 수 있는 타입은 포함되지 않아야한다.
Exhaustiveness Checking
never 활 용에 흔치 않은 예제라 정리해봤다.
모든 타입에 대한 타입 검사를 강제하고 싶다면 다음과 같이 코드를 작성하면 된다.
p.146-147
예제
type ProductPrice = "10000" | "20000" | "5000";
const getProductName = (productPrice: ProductPrice): string => {
if (productPrice === "10000") return "배민상품권 1만 원";
if (productPrice === "20000") return "배민상품권 2만 원";
else {
exhaustiveCheck(productPrice);
return "배민상품권";
}
};
const exhaustiveCheck = (param: never) => {
throw new Error("type error!");
};
마무리
타입을 집합으로써의 개념으로 설명하는 것이 사실 이해가 잘 안됐었다. 하지만 '속성'이 아닌 '값'이라는 개념과 그것을 실마리 삼아
나름대로 이해를 할 수 있었다. 합집합과 교집합은 값으로써의 합집합과 교집합의 개념이다. 첨부한 stackoverflow의 링크에
자기 스스로 이해가 잘 되는 답변을 채택해서 다른 사람에게 설명할 수 있 으면 될 것 같다.
타입 좁히기는 instanceof나 if문으로 값이 있는지 없는지 체크해서 막는 것이었는데 typeof나 in, is와 같은 자바스크립트 연산자를 활용한
타입 가드가 있다는 사실을 새롭게 배우게 되었다.
식별할 수 있는 유니온은 '이펙티브 타입스크립트'에서 본 개념이지만 실무에선 활용하지 못했었습니다. 그러나 Error 예제를 보고 나니 회사의 코드에 적용해 볼 수 있을 것 같다는 생각이 들었다.
마지막으로 never는 별로 실용적이거나 활용할 일이 없다고 생각했는데 챕터 마지막에 소개된 exhaustiveCheck를 never를
활용하기도 하는구나 하는 생각이 들었다. 예전에 Toast UI blog에서 타입스크립트의 Never 타입 완벽 가이드라는 글을 읽었던 적이 있는데,
사실 이게 뭐지? 라는 생각이 들었지만 이번에 실용적인 쓰임세를 알게되었다. 책을 다 읽었을 때 실무적으로 조금 더 완전하게 타입스크립트를 사용할 수 있을 것 같은 기대감이 생겼다.