티스토리 뷰
5.1 조건부 타입
5.1.1 extends와 제네릭을 활용한 조건부 타입
- T extends U ? X : Y
- 타입 T를 U 에 할당할 수 있으면 X 타입, 아니면 Y 타입으로 결정된다.
interface Bank {
financialCode: string;
companyName: string;
name: string;
fullName: string;
}
interface Card {
financialCode: string;
companyName: string;
name: string;
appCardType: string;
}
type PayMethod<T> = T extends "card" ? Card: Bank;
type CardPayMethod = PayMethod<"card">;
type BankPayMethod = PayMethod<"bank">;
- 제네릭과 extends를 함께 사용해 제네릭으로 받는 타입을 제한 -> 잘못된 값을 넘길 수 없기에 휴먼 에러를 방지할 수 있다.
- extends를 활용해 조건부 타입을 설정 -> 조건부 타입을 사용해서 반환 값을 사용자가 원하는 값으로 구체화할 수 있다. (불필요한 타입 가드, 타입 단언 등을 방지할 수 있다.)
5.1.2 infer를 활용해서 타입 추론하기
- extends를 사용할 때 infer 키워드를 사용할 수 있다.
- 삼항 연산자를 사용한 조건문의 형태를 가지는데, extends로 조건을 서술하고 infer로 타입을 추론하는 방식을 취한다.
// UnpackPromise 타입은 제네릭으로 T를 받아 T가 Promise로 래핑된 경우라면 K를 반환하고, 아닌 경우 any를 반환
type UnpackPromiseArray<T> = T extends Promise<infer K>[] ? K : any;
const promises = [Promise.resolve("Mark"), Promise.resolve(38)];
type Expected = UnpackPromiseArray<typeof promises>; // string | number
참고 ) https://velog.io/@from_numpy/TypeScript-infer
TypeScript - infer
조건부 타입의 조건식이 참으로 평가될 때에는 infer키워드를 사용할 수 있다. 예를 들어,Element<number> extends Element<infer U>와 같은 타입을 작성하면, U타입은 number타입으로 추론(infer)된다. 이후, 참
velog.io
5.2 템플릿 리터럴 타입 활용하기
- 템플릿 리터럴 타입은 자바스크립트의 템플릿 리터럴 문법을 사용해 특정 문자열에 대한 타입을 선언할 수 있는 기능이다.
type HeadingNumber = 1|2|3|4|5;
type HeaderTag = `h${HeadingNumber}`
- 타입스크립트 컴파일러가 유니온을 추론하는데 시간이 오래 걸리면 비효율적이기 때문에 타입스크립트가 타입을 추론하지 않고 에러를 내뱉을 때가 있다.
- 유니온 조합의 경우의 수가 너무 많지 않게 적절하게 나누어 타입을 정의하는 것이 좋다.
5.3 커스텀 유틸리티 타입 활용하기
- 유틸리티 타입을 활용한 커스텀 유틸리티 타입을 제작해서 사용할 수 있다.
5.3.1 유틸리티 함수를 활용해 styled-components 의 중복 타입 선언 피하기
Props 타입과 styled-components 타입의 중복 선언 및 문제점
- pick 유틸리티 타입을 통해 props 에서 필요한 부분만 선택하여 styled-components 컴포넌트의 타입을 정의할 수 있다.
- 중복된 코드를 작성하지 않아도 되고 유지보수를 더욱 편리하게 할 수 있다.
// HrComponent.tsx
export type Props = {
height?: string;
color?: keyof typeof colors;
isFull?: boolean;
className?: string;
...
}
export const Hr: VFC<Props> = ({height, color, isFull, className}) => {
return <HrComponent ... />;
};
// style.ts
import {Props} from "...";
type styledProps = Pick<Props, "height" | "color" | "isFull" >;
const HrComponent = styled.hr<StyledProps>`
...
`;
5.3.2 PickOne 유틸리티 함수
- 서로 다른 2개 이상의 객체를 유니온 타입으로 받을 때 타입 검사가 제대로 진행되지 않는 이슈가 있다.
- 이런 문제 해결을 위해 PickOne 이라는 유틸리티 함수를 구현해 보자!
type Card = {
card: string;
};
type Account = {
account: string;
};
function withdraw(type: Card | Account) {
...
}
withdraw({card: "hyundai", account: "hana"});
/*
타입 에러가 발생하지 않는 이유는?
집합 관점으로 볼때 유니온은 합집합이 되기 때문이다.
따라서 card, account 속성이 하나씩 할당된 상태도 허용하지만, card / account 속성이 모두 포함되어도
합집합의 범주에 들어가기에 타입 에러가 발생하지 않는다.
*/
식별할 수 있는 유니온으로 객체 타입을 유니온으로 받기
- 식별할 수 있는 유니온(Discriminated unions)은 각 타입에 type이라는 공통된 속성을 추가하여 구분하는 방법이다.
- type을 일일이 넣어줘야 하는 불편함이 생긴다.
- 이미 구현된 상태에서 적용하게 되면 해당 함수를 사용하는 부분을 모두 수정해야 한다.
type Card = {
type: "card";
card: string;
};
type Account = {
type: "account";
account: string;
};
function withdraw(type: Card | Account) {
...
}
withdraw({type: "card" , card: "hyundai"});
withdraw({type: "account", account: "hana"});
PickOne 커스텀 유틸리티 타입 구현하기
- 구현하고자 하는 타입
- account 또는 card 속성 하나만 존재하는 객체를 받는 타입이다.
- account 일 때는 card를 받지 못하고, card 일 때는 account를 받지 못하게 하려면 하나의 속성이 들어왔을 때 다른 타입을 옵셔널 한 undefined 값으로 지정하는 방법을 생각해 볼 수 있다.
{ account: string; card?: undefined} | {account?: undefined; card: string;}
type PayMethod =
| {account: string; card?: undefined; payMoney?: undefined}
| {account?: undefined; card: string; payMoney?: undefined}
| {account?: undefined; card?: undefined; payMoney: string}
→ 결국, 하나의 속성을 제외하고 나머지 값을 옵셔널 타입 + undefined로 설정하면 원하고자 하는 속성만 받도록 구현할 수 있다.
PickOne 살펴보기
type PickOne<T> = {
[P in keyof T]: Record<P, T[P]> & Partial<Record<Exclude<keyof T, P>, undefined>>;
}[keyof T];
- T 에는 객체가 들어온다고 가장하자!
type One<T> = {[P in keyof T]: Record<P, T[P]>}[keyof T];
/*
1. [P in keyof T] 에서는 T 는 객체로 가정하기의 P는 T의 객체 키 값이다.
2. Record<P, T[P]> 는 P를 키 로 가지고, P를 키로둔 T 객체의 값의 레코드 타입이다.
3. {[P in keyof T]: Record<P, T[P]>} 키는 T 객체의 키 모음이고, value 는 해당 키의 원본 객체 T 이다.
4. 3의 타입에서 다시 [keyof T] 의 키값으로 접근하기에 최종결과는 전달받은 T와 같다.
*/
type Card = { card: string };
const one: One<Card> = { card: "hyundai" }
type ExcludeOne<T> = {[P in keyof T] : Partial<Record<Exclude<keyof T, P>, undefined>>;
}[keyof T];
/*
1. [P in keyof T] 에서는 T 는 객체로 가정하기의 P는 T의 객체 키 값이다.
2. Exclude<keyof T, P> 는 T 객체가 가진 키 값에서 P타입과 일치하는 키값을 제거 한다.
3. Record<A, undefined> 키로 를, 값으로 undefined 타입을 갖는 레코드 타입이다.
즉, 전달 받은 객체 타입을 모두 {[key] : undefined } 형태로 만든다.
4. Partial<B>는 B타입을 옵셔널로 만든다. 따라서 {[key]? : undefined} 와 같다.
5. [P in keyof T]로 매핑된 타입에서 동일한 객체인 [keyof T] 로 접근하기에 4번 타입이 반환된다.
최종적으로 4번 타입이 반환된다.
*/
type PickOne<T> = One<T> & ExcludeOne<T>;
/*
1. [P in keyof T] 를 공통으로 갖기 때문에 아래와 같이 교차
2. 아래 타입을 해설하면 전달된 T 타입의 1개의 키는 값을 가지고 있으며, 나머지 키는 옵셔널한 undefined 객체를 의미한다.
*/
[P in keyof T]: Record<P, T[P]> & Partial<Record<Exclude<keyof T, P>, undefined>>;
5.3.3 NonNullable 타입 검사 함수를 사용하여 간편하게 타입 가드하기
- null을 가질 수 있는 값의 null 처리는 자주 사용되는 타입 가드 패턴의 하나이다.
NonNullable 타입이란
- 유틸리티 타입으로 제네릭으로 받는 T가 null 또는 undefined 일 때, never 또는 T를 반환하는 타입이다.
- 해당 타입을 사용하면 null이나 undefined 가 아닌 경우를 제외할 수 있다.
type NonNullable<T> = T extends null | undefined ? never | T;
null, undefined를 검사해 주는 NonNullable 함수
- NonNullable 유틸리티 타입을 사용하여 null 또는 undefined를 검사해주는 타입 가드 함수를 만들어 쓸 수 있다.
- 매개변수인 value 가 null 또는 undefined 라면 false를 반환한다.
- is 키워드가 쓰였기에 NonNullable 함수를 사용하는 쪽에서 true 가 반환된다면 넘겨준 인자는 null이나 undefined 가 아닌 타입으로 타입가드가 된다.
function NonNullable<T>(value: T): value is NonNullable<T> {
return value !== null && value !== undefined;
}
5.4 불변 객체 타입으로 활용하기
const colors = {
red: '#F45452',
green: '#0C952A',
blue: '#1A7CFF'
};
// key 의 타입을 string 으로 선언하여 colors 에 어떤 값이 추가될 지 모르기에 getColorHex 반환값은 any 가 된다.
const getColorHex = (key: string) => colors[key];
- as const 키워드로 불변 객체를 선언하고, keyof 연산자를 사용하여 getColorHex 함수 인자로 실제로 colors 객체에 존재하는 키 값만 받도록 설정할 수 있다.
- 타입에 맞지 않는 값을 전달할 경우 타입 에러가 반환되기에 컴파일 단계에서 발생할 수 있는 실수를 방지할 수 있다.
- 자동완성 기능을 통해 객체 어떤 값이 있는지 쉽게 파악할 수 있다.
5.4.1 Atom 컴포넌트에서 theme style 객체 활용하기
- Atom 단위의 작은 컴포넌트는 다양한 환경에서 유연하게 구현되어야 한다.
- 프로젝트의 스타일 값을 관리해 주는 theme 객체를 두고 관리한다.
- Atom 컴포넌트에서는 theme 객체의 색상, 폰트 사이즈의 키값을 받은 뒤 theme 객체에서 값을 받아오도록 설계한다.
타입스크립트 keyof 연산자로 객체의 키 값을 타입으로 추론하기
- keyof 연산자는 객체 타입을 받아 해당 객체의 키 값을 string 또는 number의 리터럴 유니온 타입을 반환한다.
- 인덱스 시그니처가 사용되었다면 인덱스 시그니처의 키 타입을 반환한다.
interface ColorType {
red: string;
green: string;
blue: string;
}
type ColorKeyType = keyof ColorType; // 'red' | 'green' | 'blue'
타입스크립트 typeof 연산자로 값을 타입으로 다루기
- 변수 혹은 속성의 타입을 추론하는 역할을 한다.
- typeof 연산자는 단독으로 사용되기보다 주로 유틸리티 타입이나 keyof 연산자 같이 타입을 받는 연산자와 함께 쓰인다.
const colors = {
red: '#F45452',
green: '#0C952A',
blue: '#1A7CFF'
};
type ColorsType = typeof colors;
/*
{
red: string;
green: string;
blue: string;
}
*/
객체의 타입을 활용해서 컴포넌트 구현하기
- theme 객체로 타입을 구체화
const colors = {
red: '#F45452',
green: '#0C952A',
blue: '#1A7CFF'
};
const theme = {
colors : {
default : colors.gray,
...colors
},
backgroundColor : {
default : colors.white,
gray: colors.gray,
mint : colors.mint,
black : colors.black,
}
};
type ColorType = keyof typeof theme.colors;
type BackgroundColorType = keyof typeof theme.backgroundColor;
interface Props {
color?: ColorType;
backgroundColor?: BackgroundColorType;
}
참고) https://inpa.tistory.com/entry/TS-📘-타입스크립트-keyof-typeof-사용법
📘 객체를 타입으로 변환 - keyof / typeof 사용법
타입스크립트 - keyof / typeof typeof 연산자 typeof : 객체 데이터를 객체 타입으로 변환해주는 연산자 아래의 코드의 obj는 객체이기 때문에, 당연히 객체 자체를 타입으로 사용할 수 없다. 그래서 만
inpa.tistory.com
5.5 Record 원시 타입 키 개선하기
5.5.1 무한한 키를 집합으로 가지는 Record
type Category = string;
interface Food {
name: string
}
const foodByCategory: Record<Category, Food[]> = {
한식 : [{name: "김찌"}, {name: "된찌"}],
일식 : [{name: "초밥"}, {name: "텐동"}]
}
// foodByCategory["양식"]을 Food[] 로 추론
// foodByCategory["양식"] 은 런타임에서 undefined 가 되어 오류 반환
foodByCategory["양식"].map((food) => console.log(food));
// 옵셔널 체이닝을 통해 런타임에러를 방지할 수 있지만 어떤 값이 undefined 인지 판단해야 하는 번거로움이 있다.
foodByCategory["양식"]?.map((food) => console.log(food));
5.5.2 유닛 타입으로 변경하기
- 키가 유한한 집합이라면 유닛 타입을 사용할 수 있다.
- 키가 무한해야 하는 상황에서는 적합하지 않다.
type Category = "한식" | "일식";
interface Food {
name: string
}
const foodByCategory: Record<Category, Food[]> = {
한식 : [{name: "김찌"}, {name: "된찌"}],
일식 : [{name: "초밥"}, {name: "텐동"}]
}
5.5.3 Partial을 활용하여 정확한 타입 표현하기
- 키가 무한한 상황에서는 Partial을 사용하여 해당 값이 undefined 일 수 있는 상태임을 표현할 수 있다.
type PartialRecord<K extends string, T> = Partial<Record<K,T>>;
type Category = string;
interface Food {
name: string;
}
const foodByCategory: PartialRecord<Category, Food[]> = {
한식 : [{name: "김찌"}, {name: "된찌"}],
일식 : [{name: "초밥"}, {name: "텐동"}]
}
foodByCategory["양식"]; // Food[] 또는 undefined 타입으로 추론 (사전 조치 가능)
Documentation - Utility Types
Types which are globally included in TypeScript
www.typescriptlang.org
'study > typescript' 카테고리의 다른 글
우아한 타입스크립트 6장 타입스크립트 컴파일 (0) | 2025.02.23 |
---|---|
우아한 타입스크립트 4장 타입 확장하기 (0) | 2025.02.09 |
우아한 타입스크립트 3장 고급 타입 (1) | 2025.02.03 |
우아한 타입스크립트 2장 타입 (0) | 2025.02.03 |
- Total
- Today
- Yesterday
- IOS
- 타입스크립트파일
- 스파르타코딩클럽후기
- 비전공자코딩
- ios앱개발
- javascript
- 스파르타코딩클럽
- 1시간만에끝내는직장인코딩용어해설
- iOS 앱개발 기초반
- 우아한타입스크립트
- 코딩교육
- mongodb
- Selenium
- 스파르타 코딩클럽
- 아이디어
- 타입활용
- 공부하자...
- 웹 스크래핑
- 개발자멘토
- 타입스크립트
- 내일배움단
- 크롤링
- 타입확장하기
- 내일배움카드
- 개발일지
- 코딩
- homebrew
- 르탄즈1기
- DOIT
- 스파르타코딩클럽 #비전공자코딩 #아이디어 #개발자멘토 #르탄즈 1기
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |