1. 타입 확장하기
타입 확장은 기존 타입을 사용해서 새로운 타입을 정의하는 것이다.
타입 확장의 장점은 1. 확장성 2. 코드 중복 방지이다
- 유니온 타입
2개 이상의 타입을 조합하여 사용하는 방법 (합집합의 개념) - 교차 타입
기존 타입을 합쳐 필요한 모든 기능을 가진 하나의 타입(교집합의 개념)
type IdType = string | number; type Numeric = number | boolean; type Universal = IdType & Numeric; // number
- extends
유니온과 교차 타입을 사용하여 새 타입을 만드는 것은 type 키워드에서만 가능.
interface에서는 extends로 교차 타입을 작성한다.
interface BaseMenuItem { itemName: string | null; itemImageUrl: string | null; } interface BaseCartItem extends BaseMenuItem { quantity: number; }
하지만 차이점은 있다.
extends에서는 중첩되는 키값의 타입이 다른 경우 에러가 뜨지만, 교차타입에서는 never타입이 된다.interface DelTip { tip: number; } interface Filter extends DelTip { tip: string; //'Filter' 인터페이스가 'DelTip' 인터페이스를 잘못 확장합니다. //'tip' 속성의 형식이 호환되지 않습니다. } type DelTip2 = { tip: number }; type Fliter2 = DelTip2 & { tip: string }; // never타입
확장을 통해서 새로운 타입을 만들 경우, 네이밍의 의도를 명확히 표현할 수도 있고, 작성 단계에서 예상치 못한 에러도 예방할 수 있다.
2. 타입 좁히기(타입 가드)
타입 좁히기는 변수 또는 표현식의 타입 범위를 더 작은 범위로 좁혀나가는 과정을 말한다.
자바스크립트 연산자를 활용한 타입가드는 typeof, instanceof ,in 같은 연산자를 활용하여 제어문으로 특정 타입 값을 가질 수 밖에 없는 상황을 유도하여 좁히는 방법이 있다.
- 원시 타입을 추론할 떄 : typeof 연산자 활용
string, number, boolean, undefined, object, function, bigint, symbol의 경우 typeof를 통해 타입을 좁힐 수 있다.
const replaceHyphen: (date: string | Date) => string | Date = (date) => { if (typeof date === 'string') { //이 분기에서는 date 타입이 string이다. return date.replace(/-/g, '/'); } return date; };
- 인스턴스화된 객체 타입을 판별할 때: instanceof 연산자 활용
인스턴스화된 객체 타입을 판별하는 타입 가드로 쓸 수 있다.
const onKeyDown = (event: KeyboardEvent) => { if (event.target instanceof HTMLInputElement && event.key === 'Enter') { event.target.blur(); } };
- 객체의 속성이 있는지 없는지에 따른 구분 : in 연산자 활용
in 연산자는 객체의 속성이 있는지 확인한 다음 true/false를 반환한다. 이를 통해 속성 여부에 따른 객체 타입 구분을 할 수 있다.
type Fish = { swim: () => void }; type Bird = { fly: () => void }; function move(animal: Fish | Bird) { if ('swim' in animal) { return animal.swim(); } return animal.fly(); }
- is 연산자로 사용자 정의 타입 가드 만들어 활용하기 (타입 명제 활용)
반환 타입이 타입 명제인 함수를 정의하여 사용하는 방식
type Fish = { swim: () => void }; type Bird = { fly: () => void }; //is function isFish(pet: Fish | Bird): pet is Fish { return (pet as Fish).swim !== undefined; } function getSmallPet(): Fish | Bird { const randomNumber = Math.random(); if (randomNumber < 0.5) { return { swim: () => console.log('Fish is swimming') }; } else { return { fly: () => console.log('Bird is flying') }; } } let pet = getSmallPet(); if (isFish(pet)) { pet.swim(); } else { pet.fly(); }
3. 타입 좁히기(식별할 수 있는 유니온)
태그된 유니온 혹은 식별할 수 있는 유니온으로도 타입을 좁힐 수 있다.
아래에는 자바스크릡트가 덕 타이핑 언어이기 때문에 별도의 타입 에러를 발생하지 않는 wrongErrorArr이 있다.
존재하지 않는 타입이지만 에러가 생기지 않았고, 이럴 경우 무수한 에러 객체가 생겨날 위험이 생긴다.
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: () => {} },
];
const wrongErrorArr: ErrorFeedbackType[] = [
{
errorCode: '900',
errorMessage: '잘못된 에러',
toastShowDuration: 3000,
onConfirm: () => {},
},
];
이를 해결하기 위해서 에러 타입을 구분할 필요가 있는데, 비슷한 구조를 가지지만 서로 호완되지 않도록 만들어주기 위해서는 타입들이 서로 포함 관계를 가지지 않도록 정의해야 한다. 이때 사용하는 것이 식별할 수 있는 유니온 이다.
식별할 수 있는 유니온
타입간의 구조 호환을 막기 위해 타입마다 구분할 수 있는 판별자를 달아 주어 포함 관계를 제거하는 것
type TextError2 = {
errorType: 'Text';
errorCode: string;
errorMessage: string;
};
type ToastError2 = {
errorType: 'Toast';
errorCode: string;
errorMessage: string;
toastShowDuration: number; //토스트 시간
};
type AlertError2 = {
errorType: 'Alert';
errorCode: string;
errorMessage: string;
onConfirm: () => void; // 확인 버튼 후 액션
};
type ErrorFeedbackType2 = TextError2 | ToastError2 | AlertError2;
const errorArr2: ErrorFeedbackType2[] = [
{ errorType: 'Text', errorCode: '100', errorMessage: '텍스트' },
{
errorType: 'Toast',
errorCode: '200',
errorMessage: '토스트',
toastShowDuration: 3000,
},
{
errorType: 'Alert',
errorCode: '300',
errorMessage: '앨럿',
onConfirm: () => {},
},
];
const wrongErrorArr2: ErrorFeedbackType2[] = [
{
errorType: 'Toast',
errorCode: '900',
errorMessage: '잘못된 에러',
toastShowDuration: 3000,
// onConfirm: () => {}, //개체 리터럴은 알려진 속성만 지정할 수 있으며 'ToastError2' 형식에 'onConfirm'이(가) 없습니다
},
];
errorType을 통해서 구별할 수 있게 된다.
이때 식별할 수 있는 유니온을 선정할 때는 판별자는 유닛 타입으로 선언되어야 정상적으로 작동한다.
유닛 타입 : 오직 하나의 정확한 값을 가지는 타입 (null, undefined, 리터럴 타입,1 등등)
공식 깃허브에서는 2가지 조건을 건다
- 리터럴 타입
- 판별자로 선정한 값ㅇ에 적어도 하나 이상의 유닛 타입이 포함되어야 하며, 인스턴스화 할 수 있는 타입은 포함되면 안된다.
4. Exhaustiveness Checking 으로 정확한 타입 분기 유지하기
모든 케이스에 대해서 철저하게 타입을 검사하는 것을 말하며 타입 좁히기에 사용되는 패러다임 중 하나
Exhuastiveness Checking을 통해 모든 케이스에 대한 타입 검사를 강제할 수 있다.
const getProduction = (productPrice: ProductPrice): string => {
if (productPrice === '10000') return '배민상품권 1만원';
if (productPrice === '20000') return '배민상품권 2만원';
else exhaustiveCheck(productPrice); //'string' 형식의 인수는 'never' 형식의 매개 변수에 할당될 수 없습니다.ts(2345)
return '배민상품권';
};
const getProduction2 = (productPrice: ProductPrice): string => {
if (productPrice === '10000') return '배민상품권 1만원';
if (productPrice === '20000') return '배민상품권 2만원';
if (productPrice === '50000') return '배민상품권 5만원';
else {
exhaustiveCheck(productPrice);
return '배민상품권';
}
};
const exhaustiveCheck = (param: never) => {
throw new Error('type Error!');
};
이 부분에 대해서는 exhaustiveCheck 함수가 런타임 코드에 포함되기 때문에 의견이 분분하지만, 우형 이야기에서는 성능에 거의 영향도 없고, 테스트 코드로 해결할 수 있는 부분도 있지만 소통 측면에서 매력적이다고 하였습니다.
var getProduction2 = function (productPrice) {
if (productPrice === '10000')
return '배민상품권 1만원';
if (productPrice === '20000')
return '배민상품권 2만원';
if (productPrice === '50000')
return '배민상품권 5만원';
else {
exhaustiveCheck(productPrice);
return '배민상품권';
}
};
var exhaustiveCheck = function (param) {
throw new Error('type Error!');
};
JS파일에도 exhaustiveCheck 함수가 남아 있다.
'독서' 카테고리의 다른 글
우아한 타입스크립트 with React -6장 타입스크립트 컴파일 (0) | 2024.02.19 |
---|---|
우아한 타입스크립트 with React -5장 타입활용하기 (0) | 2024.02.17 |
우아한 타입스크립트 with React -3장 고급타입 (0) | 2024.02.14 |
우아한 타입스크립트 with React -2장 타입 (0) | 2024.02.13 |
우아한 타입스크립트 with React -1장 들어가며 (0) | 2024.02.11 |