일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- async/await
- webpack
- useAppDispatch
- CI/CD
- CORS
- 타입 좁히기
- 무한 스크롤
- 이분 검색
- 인터섹션
- SSR
- 반공변성
- RTK Query
- map
- 결정 알고리즘
- 태그된 유니온
- recoil
- TS
- Jest
- ESlint
- dfs
- tailwind
- Cypress
- app router
- React
- 공변성
- 투포인터
- autosize
- Promise
- 호이스팅
- 리터럴 타입
- Today
- Total
짧은코딩
타입 반복 줄이기(필독) 본문
타입 반복
개발자라면 코드의 반복을 줄이려고 노력한 적이 분명 있을 것이다. 이런 것을 DRY(don't repeat yourself)라고 한다. 이것을 타입에도 적용하여 타입 반복 또한 줄여야 한다.
이 글은 두고두고 많이 봐야 한다고 생각한다. 왜냐하면 평소에 코드 중복을 굉장히 많이 하여 개발했기 때문이다. 그렇기에 개발을 할 때마다 이 글은 자주 챙겨보면서 타입 중복을 피하여 코딩해야겠다.
타입 반복 예시
-문제 코드
interface Person {
firstName: string
lastName: string
}
interface PersonWithBirthDate {
firstName: string
lastName: string
birth: Date
}
나도 평소에 이렇게 타입이 겹치는 것이 있더라도 따로 분리하여 타입을 중복시켰다.
타입 중복 해결 방법은 중복되는 시그니처를 명명된 타입으로 분리하거나 extends을 이용해 확장하면 된다.
-올바른 코드
interface Person {
firstName: string
lastName: string
}
interface PersonWithBirthDate extends Person {
birth: Date
}
이렇게 extends를 사용해 타입 확장을 할 수 있다.
type PersonWithBirthDate = Person & { birth: Date };
혹은 이미 존재하는 타입을 확장하면 일반적이진 않지만 인터섹션 연산자 "&"를 이용할 수 있다. 이것은 확장할 수 없는 유니온 타입에 속성을 추가할 때 유용하다.
확장이 애매한 경우(Pick)
interface State {
userId: string
pageTitle: string
recentFiles: string[]
pageContents: string
}
interface TopNavState {
userId: string
pageTitle: string
recentFiles: string[]
}
만약 State는 전체 애플리케이션의 상태이고 TopNavState는 부분만 표현하는 상태라면 TopNavState를 확장하여 State를 구성하는 것보다 State에서 속성을 빼내서 TopNavState를 구성하는 것이 좋다.
-TopNavState
interface State {
userId: string
pageTitle: string
recentFiles: string[]
pageContents: string
}
// 1번
type TopNavState = {
userId: State['userId']
pageTitle: State['pageTitle']
recentFiles: State['recentFiles']
}
// 2번
type TopNavState = {
[k in 'userId' | 'pageTitle' | 'recentFiles']: State[k]
}
맨 처음엔 1번 방식으로 줄일 수 있겠지만 결국엔 2번 방식으로 하는 것이 코드 중복을 최소화하는 코드이다.
2번 코드에 마우스를 올리면 결국 똑같은 구성을 하고 있다는 것을 알 수 있다.
이러한 방식은 배열의 필드를 루프 도는 것과 같은 방식이다. 이 패턴은 표준 라이브러리에서 볼 수 있으며 Pick라고 한다.
-Pick
type TopNavState = Pick<State, 'userId' | 'pageTitle' | 'recentFiles'>
Pick로는 이렇게 구현할 수 있다. Pick는 제네릭 타입이며 두 개의 매개변수를 받아 결괏값을 반환하여 함수를 호출하는 것과 같은 효과이다.
최소 중복으로 선택적 필드 만들기, 객체의 타입 만들기(keyof, Partial)
-keyof, Partial
keyof는 타입을 받아서 속성 타입의 유니온을 반환한다. 매핑된 타입인 [K in keyof Options]는 Options를 순회하며 K 값에 해당하는 속성이 있는지 찾는다. 이것 또한 표준 라이브러리에서 Partial이라는 이름으로 있다.
-최소 중복으로 선택적 필드 만들기
interface Options {
width: number
height: number
color: string
label: string
}
interface OptionsUpdate {
width?: number
height?: number
color?: string
label?: string
}
선택적 필드도 타입 중복을 최소화해서 만들 수 있다. 매핑된 타입과 keyof를 사용하면 Options로부터 OptionsUpdate를 만들 수 있다.
type OptionsUpdate = { [k in keyof Options]?: Options[k] }
이런 식으로 keyof를 활용하여 선택적 필드를 구현할 수 있다.
-객체의 타입 만들기
const INIT_OPTIONS = {
width: 640,
height: 480,
color: '#00FF00',
label: 'VGA',
}
type Options = typeof INIT_OPTIONS
이렇게 INIT_OPTIONS 객체를 타입으로 만들고 싶으면 typeof를 사용하면 된다.
Options에 마우스를 올리면 이렇게 타입이 나오게 된다.
함수나 메서드 반환 값으로 타입 만들기(ReturnType)
function getUserInfo(userId: string) {
// COMPRESS
const name = 'Bob'
const age = 12
const height = 48
const weight = 70
const favoriteColor = 'blue'
// END
return {
userId,
name,
age,
height,
weight,
favoriteColor,
}
}
type UserInfo = ReturnType<typeof getUserInfo>
이렇게 return 값의 타입을 선언하고 싶으면 ReturnType을 사용하면 된다.
이렇게 마우스를 올려보면 타입이 다 들어간 것을 볼 수 있다.
'TS > 이펙티브 타입스크립트' 카테고리의 다른 글
객체 생성하기 (0) | 2023.03.07 |
---|---|
타입 넓히기와 좁히기 (0) | 2023.03.05 |
type과 interface (0) | 2023.02.21 |
객체 래퍼 타입 지양하기 & 함수 표현식과 타입스크립트 (0) | 2023.02.21 |
타입 선언문과 단언문 (0) | 2023.02.10 |