1. 문제 상황
React에서 기존 태그를 감싼 Custom Component를 만들때, 기존 태그의 속성을 override하고 싶은 경우가 있다. 그때 styled-components 를 이용하는 경우 어떻게 타입 선언을 해야할까?
2. 방법
적당한 예시와 함께 보자.
input 태그의 size 속성은 input의 width를 결정한다.
우리가 만들 Custom Component인 Input은 size 속성에 따라 다른 padding 을 설정하는 것으로 바꾸고 싶다.
거두절미하고 결과코드부터 보자.
결과코드
// Input
import { ComponentPropsWithoutRef } from "react";
import styled, { css } from "styled-components";
export type Props = {
size?: "small" | "medium" | "large";
} & Omit<ComponentPropsWithoutRef<"input">, "size">; // input 태그의 기존 size 속성을 제거한 타입을 상속한다
const Input = ({ size = "medium", ...rest }: Props) => {
return <StyledInput _size={size} {...rest}></StyledInput>; // _size에 size 할당
};
export default Input;
const genSizeStyle = (size: Required<Props>["size"]) => {
const styles = {
small: css`
padding: 0px 7px;
`,
medium: css`
padding: 4px 11px;
`,
large: css`
padding: 7px 11px;
`
};
return styles[size];
};
// 기존 input size 타입:(number | undefined) & _size:("small" | "medium" | "large" | undefined)
// => 확장
const StyledInput = styled.input<Props & { _size: Props["size"] }>`
${({ _size = "medium" }) => genSizeStyle(_size)};
`;
눈여겨 볼 부분은 2가지다.
첫번째는 Input Props 타입을 선언할 때, 기존 input 태그 타입에서 size를 제거한 타입을 상속받아야한다.
두번째는 styled-component 인 StyledInput 을 선언할 때 generic 타입을 선언하는 부분이다.
코드를 보면 Props 타입과 _size 필드를 가진 객체 타입을 &(intersection) 하고 있다.
즉 순정 input 태그의 기존 size 필드는 그대로 두고, _size 필드만 추가한 확장 타입을 generic으로 주어서 해결한다.
밑 코드예시 처럼 선언하면 안되는 이유는, 객체 type & (intersection)의 동작에 있다.
// 순정 input의 size 타입:(number | undefined) & Props의 size 타입:("small" | "medium" | "large" | undefined)
// => undefined
const StyledInput = styled.input<Props>`
${({ size = "medium" }) => genSizeStyle(size)}; // size: undefined
`;
겹치는 타입이 undefined 밖에 없으므로, size는 undefined 타입이 된다.
그래서 어쩔 수 없이 styled-component StyledInput내부적으로 _size 를 추가하는 꼼수..(?)를 이용했다.
어차피 Input 태그를 이용하는 쪽에서는 size 속성을 이용하므로
원래 목적(styled-components를 이용할 때 기존 태그의 attributes를 override하기)은 잘 달성했다.
3. 직접 테스트해보기
여기서 직접 테스트해보자.
'프론트엔드' 카테고리의 다른 글
[번들 사이즈 최적화] 웹팩으로 생성한 번들 파일을 분석해보자(BundleAnalyzerPlugin) (0) | 2023.09.11 |
---|---|
회원가입 폼에서 주로 쓰이는 필수입력사항에* 별표 표시하기 (0) | 2023.07.16 |
KDC 한국십진분류표 (0) | 2023.06.21 |
컴포넌트를 만들 때 기존 태그의 props를 상속받는 법 (0) | 2023.06.17 |
typescript로 상수와 타입을 선언하는 방법: as const, enum (0) | 2023.06.16 |