유틸리티 타입을 활용하여 타입 중복 선언 제거
- Props 타입과 styled-components 타입은 똑같은 타입으로 두 타입을 각각 선언하면 중복된 코드가 생긴다. 그리고 타입이 변경되면 두 타입을 모두 변경해줘야 하는 번거러움이 발생한다.
- 유틸리티 타입인
Pick
을 활용하면 중복된 코드를 작성하지 않아도 되고 유지보수를 더욱 편리하게 할 수 있다. 이외에도 상속받는 컴포넌트 혹은 부모 컴포넌트에서 자식 컴포넌트로 넘겨주는 props 등에도 활용할 수 있다.
| |
| export type Props = { |
| height?: string; |
| color?: string; |
| isFull?: boolean; |
| className?: string; |
| }; |
| |
| const Hr: FC<Props> = ({ height, color, isFull, className }) => { |
| return ( |
| <HrComponent |
| height={height} |
| color={color} |
| isFull={isFull} |
| className={className} |
| /> |
| ); |
| }; |
| |
| |
| type StyledProps = Pick<Props, 'height' | 'color' | 'isFull'>; |
| |
| export const HrComponent = styled.hr<StyledProps>` |
| height: ${({ height }) => height || '10px'}; |
| margin: 0; |
| background-color: ${({ color }) => color || 'gray'}; |
| border: none; |
| ${({ isFull }) => |
| isFull && |
| css` |
| margin: 0 -15px; |
| `} |
| `; |
커스텀 유틸리티 타입 구현
- 타입스크립트에는 서로 다른 2개 이상의 객체를 유니온 타입으로 받을 때 타입 검사기가 제대로 진행되지 않는 문제가 있다.
- 이는 집합 관점에서 볼 때 유니온은 합집합이 되기 때문이다. 모든 객체의 속성이 모두 포함되어도 합집합의 범주에 들어가기 때문에 타입 에러가 발생하지 않는다.
| type Card = { |
| card: string; |
| }; |
| |
| type Account = { |
| account: string; |
| }; |
| |
| function withdraw(type: Card | Account) { |
| |
| } |
| |
| withdraw({ card: '신한', account: '하나' }); |
- 식별할 수 있는 유니온으로 문제를 해결할 수 있지만 일일이 판별자를 넣어줘야 하는 불편함이 발생한다. 이미 구현된 상태에서 식별할 수 있는 유니온을 적용하려면 해당 함수를 사용하는 부분을 모두 수정해야 한다. 실수로 수정하지 않은 부분이 생긴다면 또 다른 문제가 발생할 수 있다.
| type Card = { |
| type: 'card'; |
| card: string; |
| }; |
| |
| type Account = { |
| type: 'account'; |
| account: string; |
| }; |
| |
| function withdraw(type: Card | Account) { |
| |
| } |
| |
| withdraw({ type: 'card', card: '신한' }); |
| withdraw({ type: 'account', account: '하나' }); |
- 하나의 속성을 제외한 나머지 값을
옵셔널 타입 + undefined
로 설정하면 원하고자 하는 속성만 받도록 구현할 수 있다.
| type Card = { card: string }; |
| type Account = { account: string }; |
| |
| type One<T> = { [P in keyof T]: Record<P, T[P]> }[keyof T]; |
| const one: One<Card> = { card: '신한' }; |
| |
| type ExcludeOne<T> = { |
| [P in keyof T]: Partial<Record<Exclude<keyof T, P>, undefined>>; |
| }[keyof T]; |
| |
| |
| type PickOne<T> = { |
| [P in keyof T]: Record<P, T[P]> & |
| Partial<Record<Exclude<keyof T, P>, undefined>>; |
| }[keyof T]; |
| |
| const pickOne1: PickOne<Card & Account> = { card: '신한' }; |
| const pickOne2: PickOne<Card & Account> = { account: '하나' }; |
| const pickOne3: PickOne<Card & Account> = { card: '신한', account: undefined }; |
| const pickOne4: PickOne<Card & Account> = { card: undefined, account: '하나' }; |
| |
| type CardOrAccount = PickOne<Card & Account>; |
| |
| function withdraw(type: CardOrAccount) { |
| |
| } |
| |
| withdraw({ card: '신한', account: '하나' }); |