typescript + styled-components 조합을 사용하면서 컴포넌트 속성을 정의해야했다.
Chip, StyledChip 각각 두 속성을 정의했다.
export interface ChipProps {
label: string;
selected: boolean;
onClick: (e: React.MouseEvent<HTMLInputElement>) => void;
}
export interface StyledChipProps {
selected: boolean;
}
중복하는 속성이 있으면서도 연관이 있는 타입들인데 꼭 따로 정의해야하나라는 생각이 들었다.
타입을 하나만 정의하고 selected 속성만 선택적으로 사용하는 방법은 없을까를 고민하게 됐다.
아래 방법들을 통해 고민한 문제를 개선할 수 있었고, 그 중 가장 나은 방법이 typescript의 유틸리티 타입인 Pick였다.
export interface ChipProps {
label?: string;
selected: boolean;
onClick: (e: React.MouseEvent<HTMLInputElement>) => void;
}
const StyledChip = styled.div<ChipProps>`
//...
`;
export default function Chip({ label, selected, onClick }: ChipProps) {
return (
<StyledChip selected={selected} onClick={onClick}>
{label}
</StyledChip>
);
}
label이 StyledChip을 위해서 옵셔널 해졌는데, 이런 변경점으로 Chip의 label 까지도 옵셔널 하게 됐다.
이 말은 Chip에 label값이 전달되지 않을 수도 있다는 거고, Chip 컴포넌트 제작자와의 의도(클릭가능하며 라벨이 있는 chip)와는 다르게 된다.
이를 해치지 않으면서 ChipProps에서 필요한 속성만 사용할 수 있는 방법은 없을까?
const StyledChip = styled.div<{ selected: boolean }>`
// ...
`;
스타일링에 사용될 속성들을 리터럴로 정의한다.
간단한 방법이지만, 이는 ChipProps와 일관성을 유지해야하기 때문에 각각의 속성변경이 서로에게 영향을 줄 수 있다.
타입을 변환하기 위해 사용할 수 있는 유틸리티 타입을 제공하고 있다.
Pick과 Omit은 특정 Type에서 Key의 집합을 선택/제거해 타입을 생성하는 것을 도와준다.
const StyledChip = styled.div<Pick<ChipProps, 'selected'>>`
// ...
`;
조금 길긴 하지만, ChipProps Type을 일부 사용하는 거기 때문에 내부 타입들을 공유할 수 있다.
위의 문제가 개선됐다.
const StyledChip = styled.div<Omit<ChipProps, 'label, onClick'>>`
// ...
`;
Omit타입의 사용은 Pick타입과 비교해 코드를 읽기가 어려울 수 있다.
속성이 들어갈 위치에는 스타일링에 사용되는 속성이 들어가는 것이 더 용이할 것 같다.
이후 새로운 방법을 알게되면 계속 기록해나가야야겠다...