checkbox
타입의 <input> 엘리먼트를 사용하는 Toggle 컴포넌트를 만든다고 가정해보자. <input> 엘리먼트는 autoFocus
required
readOnly
등의 기본 어트리뷰트 가진다.
만약 <input> 엘리먼트가 기본적으로 받을 수 있는 어트리뷰트를 Toggle 컴포넌트의 props로 넘기고 싶다면? 모든 어트리뷰트에 대한 인터페이스를 정의해야 할까?
이땐 InputHTMLAttributes
타입을 사용하면 된다. 그럼 각 어트리뷰트의 prop 타입을 일일이 정의하지 않아도 된다. 제네릭 T
는 onChange
이벤트 핸들러의 엘리먼트 타입이 들어가므로 HTMLInputElement
같은 타입을 넘기면 된다.
interface InputHTMLAttributes<T> extends HTMLAttributes<T> {
accept?: string | undefined;
alt?: string | undefined;
autoComplete?: string | undefined;
// ...
onChange?: ChangeEventHandler<T> | undefined;
}
type ChangeEventHandler<T = Element> = EventHandler<ChangeEvent<T>>;
<aside> 💡 InputHTMLAttributes, InputAttributes 등은 @types/react 패키지에서 제공하는 타입이다
</aside>
참고로 id
className
draggable
같은 HTML 요소의 공통적인 어트리뷰트는 HTMLAttributes
타입에서 상속 받아 사용하고 있다. DOMAttributes
의 제네릭 T
는 위와 마찬가지로 엘리먼트 타입을 받는다.
interface HTMLAttributes<T> extends AriaAttributes, DOMAttributes<T> {
// React-specific Attributes
defaultChecked?: boolean | undefined;
defaultValue?: string | number | ReadonlyArray<string> | undefined;
// ...
// Standard HTML Attributes
accessKey?: string | undefined;
className?: string | undefined;
// ...
}
interface DOMAttributes<T> {
// ...
onCopy?: ClipboardEventHandler<T> | undefined;
// ...
}
<input> 엘리먼트의 기본 어트리뷰트는 InputHTMLAttributes<HTMLInputElement>
타입을 통해 상속받고, label
color
같은 컴포넌트 수준에서 필요한 prop만 ToggleProps
인터페이스에 추가해서 사용한다.
한편 size
속성은 **<input>**의 기본 어트리뷰트(number | undefined
)지만 프로젝트에서 요구하는 사이즈 타입 TSize
에 맞춰야하는 상황이다. 따라서 Omit 유틸리티 타입으로 InputHTMLAttributes
의 size
속성을 제외하고 ToggleProps
인터페이스에 TSize
타입을 추가했다.
import React, { InputHTMLAttributes } from "react";
// TColor : primary | error | background | ...
// TSize : base | lg | md | ...
interface ToggleProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
className?: string;
label?: string;
color?: TColor;
size?: TSize; // size는 <input>의 기본 어트리뷰트지만 프로젝트에서 요구하는 타입으로 대체
}
export default function Toggle({
className,
label,
color,
size,
...inputProps
}: ToggleProps) {
// ... 컴포넌트 본문 및 return문 상세 생략
return <input type='checkbox' {...inputProps} />;
}