interface BaseMenuItem {
itemName: string | null;
itemImageUrl: string| null;
itemDiscountAmout: number;
stock: number| null;
}
interface BaseCardItem extends BaseMenuItem {
quantity: number;
}
type키워드는 아래와 같이 작성할 수 있다.type BaseMenuItem = {
itemName: string | null;
itemImageUrl: string| null;
itemDiscountAmout: number;
stock: number| null;
}
type BaseCartItem = {
qantity: number;
} & BaseMenuItem
interface CookingStep {
orderId: string;
price: number;
}
interface DeliverStep {
orderId: string;
time: number;
distance: string;
}
function getDeliveryDistance(step: CookingStep | DeliverStep) {
return step.distance;
// Property 'distance' does not exist on type 'CookingStep | DeliverStep'
// Property 'distance' does not exist on type 'CookingStep
}
interface CookingStep {
orderId: string;
time: number;
price: number;
}
interface DeliveryStep {
orderId: string;
time: number;
distance: string;
}
type BaedalProgress = CookingStep & DeliveryStep;
function logBaedalInfo(progress: BaedalProgress) {
console.log(progress.price); // o
}
유니온은 합집합이라면 교차타입은 교집합의 개념과 비슷하다. 하지만 타입이 서로 호환되지 않는 경우도 있다.type IdType = string | number;
type Numeric = number | boolean;
type Universal = IdType & Numeric;
이 경우에 4가지로 생각해 볼 수 있다.string이면서 numbe인 경우
string이면서 boolean인 경우
number이면서 number인 경우
number이면서 boolean인 경우
3번만 유효하기 떄문에 Universal의 값은 number가 된다.
type BaseMenuItem = {
itemName: string | null;
itemImageUrl: string| null;
itemDiscountAmout: number;
stock: number| null;
}
type BaseCartItem = {
qantity: number;
} & BaseMenuItem
교차 타입을 사용한 코드에서는 BaseMenuItem과 BaseCartItem를 interface가 아닌 type으로 선언했다. 왜냐하면 교차 아빙느 사용한 새로운 타입은 오직 type 키워드로만 선언할 수 있기 떄문이다.interface DeliveryTip {
tip: number;
}
interface Filter extends DeliveryTip {
tip: string;
// Interface 'Filter" incorrectly extends interface 'DeliveryTip'
}
DeliveryTip의 tip과 Filter의 tip이 호환되지 않는다는 에러가 발생한다. type DeliveryTip = {
tip: number;
}
type Filter = DeliveryTip & {
tip: string;
}
type으로 변경하고 &를 사용하면 에러가 발생하지 않는데 이때 타입은 never이 된다. type은 쿄차 타입은 로 선언되었을 떄 새롭게 추가되는 속성에 대해 미리 알 수 없기 때문에 선언 시 에러가 발생하지 않는다.타입 좁히기는 변수 또는 표현식의 타입 범위를 더 작은 범위로 좁혀나가는 과정을 말한다.
타입 가드
를 활용하여 변수나 표현식의 타입 범위를 좁혀 다양한 상황에 따라 다른 동작을 수행하는 것을 말한다.타입스크립트로 개발하다 보면 여러 타입을 할당할 수 있는 스코프에서 특정 타입을 조건으로 만들어 분기 처리하고 싶을 때가 있다.💡 타입 가드
타입 가드는 런타임에 조건문을 사용하여 타입을 검사하고 타입 범위를 좁혀주는 기능을 말한다.
💡 스코프
변수와 함수 등의 식별자가 유효한 범위를 나타낸다. 즉, 변수와 함수를 선언하거나 사용할 수 있는 영역을 말한다.
자바스크립트 연산자
를 사용한 타입 가드와 사용자 정의
타입 가드로 구분된다.A instanceof B
형태로 사용하며 A 프로토타입 체인에 생성자 B가 존재하는지를 검사한다.const isDestinationCode = (x: string): x is DestinationCode => destinationCodeList.includes(x);
isDestinationCode는 string 타입의 매개변수가 destinationCodeList 배열의 원소 중 하나인지를 검사하여 boolean을 반환하는 함수이다.type TextError = {
errorCode: string;
errorMessage: string;
}
type ToastError = {
errorCode: string;
errorMessage: string;
toastMessage: string;
toastShowDuration: number; // 토스트를 띄워줄 시간
}
type AlerError = {
errorCode: string;
errorMessage: string;
onConfirm: () => void;
}
이 에러 타입의 유니온 타입을 원소로 하는 배열을 정의해보면 다음과 같다.type ErrorFeedbackType = TextError | ToastError | AlertError;
const errorArr: ErrorFeedbackType[] = [
{ errorCode: "100", errorMessage: "텍스트 에러" },
{ errorCode: "100", errorMessage: "텍스트 에러", toastShowDutaion: 3000 },
{ errorCode: "300", errorMessage: "얼럿 에러", onConfirm: () => {} }
];
errorArr는 다양한 에러 객체를 관리할 수 있게 되었다.const errorArr: ErrorFeedbackType[] = [
//...
{
errorCode: "999",
errorMessage: "잘못된 에러",
toastShowDuration: 3000,
onConfirm: () => {}
}
]
하지만 이 코드를 작성했을 때 자바스크립트는 덕 타이핑 언어이기 때문에 별도의 타입 에러를 뱉지 않는 것을 확인할 수 있다.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을 새로 정의해보자type ErrorFeedbackType = TextError | ToastError | AlertError;
const errorArr: ErrorFeedbackType[] = [
{ errorType: "TEXT", errorCode: "100", errorMessage: "텍스트 에러" },
{
errorType: "TOAST",
errorCode: "200",
errorMessage: "토스트 에러",
toastShowDuration: 3000
},
{
errorType: "TEXT",
errorCode: "999",
errorMessage: "잘못된 에러",
toastShowDuration: 3000 // Object literal may only specify known properties, and 'toastShowDutaion' does not exist in type 'TextError'
}
]
정확하지 않은 에러 객체에 대해 타입 에러가 발생하는 것을 확인할 수 있다.모든 케이스에 대해 철저하게 타입을 검사하는 것을 말한다.
type ProductPrice = "10000" | "20000";
const getProductName = (productPrice: ProductPrice): string => {
if(productPrice === "10000") return "배민상품권 1만 원";
if(productPrice === "20000") return "배민상품권 2만 원";
else {
return "배민상품권";
}
}
여기서 ProductPrice 타입이 업데이트 되어야 한다고 해보자.
type ProductPrice = "10000" | "20000" | "50000";
const getProductName = (productPrice: ProductPrice): string => {
if(productPrice === "10000") return "배민상품권 1만 원";
if(productPrice === "20000") return "배민상품권 2만 원";
if(productPrice === "50000") return "배민상품권 5만 원";
else {
return "배민상품권";
}
}
위와 타입 업데이트와 같이 조건도 검사하여 의도한 대로 상품권 이름을 반환해야 한다. 그러나 getProductName 함수를 수정하지 않아도 별도 에러가 발생하는 것이 아니기 때문에 실수할 여지가 있다. 이와 같이 모든 타입에 대한 타입 검사를 강제하고 싶다면 아래와 같이 코드를 작성할 수 있다.
type ProductPrice = "10000" | "20000" | "50000";
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!");
}