요 발표의 내용의 일부를 보충해서 정리해보았다.
React.HTMLAttributes<HTMLButtonElement>
React.HTMLProps<HTMLButtonElement>
JSX.IntrinsicElements['button']
React.ButtonHTMLAttributes<HTMLButtonElement>
React.ComponentProps<'button'>
React.ComponentPropsWithRef<'button'>
React.ComponentPropsWithoutRef<'button'>위의 많은 타입들… 이것들은 모두 button에 대한 타입을 나타낸다. 
작업하다보면 가끔 가다 확장해서 사용해야하는 케이스들이 있는데, 어떤 것을 사용해야할까?
하나 하나 살펴보자.
1. React.HTMLAttributes<HTMLButtonElement>
interface HTMLAttributes<T> extends AriaAttributes, DOMAttributes<T> {
  // React-specific Attributes
  // Standard HTML Attributes
  // RDFa Attributes (RDFa에 대해서는 나중에 적어보자)
}
interface DOMAttributes<T> {
  children?: ReactNode | undefined;
  // ...
  // event handlers... 
}제너릭으로 받아 DOMAttributes로 확장된다.
전반적으로 많은 HTML 속성을 포함하고 있지만, 특정 태그에 대한 구체적인 타입들을 제공하지 못한다.
interface ButtonProps extends React.HTMLAttributes<HTMLButtonElement> {}
function Button(props: ButtonProps) {
  // ❌ 에러: 'type' 속성은 HTMLButtonElement에 존재하지 않습니다.
  const isSubmit = props.type === 'submit';
  // ...
}2. React.HTMLProps<HTMLButtonElement>
interface HTMLProps<T> extends AllHTMLAttributes<T>, ClassAttributes<T> {
}
interface AllHTMLAttributes<T> extends HTMLAttributes<T> {
  //...
  type?: string | undefined;
  // ...
}
interface ClassAttributes<T> extends RefAttributes<T> {
}
AllHTMLAttributes 의 alias로 모든 HTML 속성을 포함하고 있다.
그래서 타입 자체도 많고, 특정 태그에 대한 타입들도 제공받지만, 덜 구체적인 타입을 제공한다.
interface ButtonProps extends React.HTMLProps<HTMLButtonElement> {}
function Button(props: ButtonProps) {
  // ❌ 
  // type?: 'button' | 'submit' | 'reset' | undefined;
  //
  // 'type' 속성이 존재하지만, 타입이 부족합니다.
  // type?: string | undefined;
  const type = props.type;
  // ...
}3. JSX.IntrinsicElements['button']
interface IntrinsicElements {
  // HTML
  // ...
  button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>;
}
type DetailedHTMLProps<E extends HTMLAttributes<T>, T> = ClassAttributes<T> & E;typescript의 글로벌 스코프에 있는 타입으로, 사용 가능한 네이티브 JSX 요소들의 타입을 제공한다.
그렇기에 내장 요소 라고도 불린다.\
하지만, 이 타입은 인터페이스 확장이 불가능하다.
// ❌ 에러: 인덱스 접근 타입은 확장할 수 없습니다.
interface ButtonProps extends JSX.IntrinsicElements['button'] {}이렇게 하면 되긴 한다..
interface ButtonProps extends NonNullable<JSX.IntrinsicElements['button'], {}> {}
4. React.ButtonHTMLAttributes<HTMLButtonElement>
interface ButtonHTMLAttributes<T> extends HTMLAttributes<T> {
    disabled?: boolean | undefined;
    form?: string | undefined;
    formAction?:
        | string
        | DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_FORM_ACTIONS[
            keyof DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_FORM_ACTIONS
        ]
        | undefined;
    formEncType?: string | undefined;
    formMethod?: string | undefined;
    formNoValidate?: boolean | undefined;
    formTarget?: string | undefined;
    name?: string | undefined;
    type?: "submit" | "reset" | "button" | undefined;
    value?: string | readonly string[] | number | undefined;
}위의 JSX.IntrinsicElements['button']의 내부에서 살펴볼 수 있었던 타입으로,
우리가 필요로 하는 것들을 제공한다. 하지만 더 유용한 타입이 있다.
여기 아주 재밌는 타입이 있다
DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_FORM_ACTIONS
RSC(React Server Components)에서 form action을 지원하기 위한 타입이다.
지금 Canaray 채널에서만 사용 가능하다. 타입 이름 그대로 지금 사용하면 해고당할 수 있다.
여기서 구체적으로 찾아보지는 않겠지만, 아래 자료들을 보면 더 자세히 알 수 있을 것이다.
- https://react.dev/reference/react-dom/components/form
- https://github.com/DefinitelyTyped/DefinitelyTyped/pull/66928
- https://github.com/facebook/react/pull/27459
- https://github.com/facebook/react/pull/27460
5. React.ComponentProps<'button'>
https://react-typescript-cheatsheet.netlify.app/docs/react-types/componentprops/
type ComponentProps<T extends keyof JSX.IntrinsicElements | JSXElementConstructor<any>> = T extends
    JSXElementConstructor<infer P> ? P
    : T extends keyof JSX.IntrinsicElements ? JSX.IntrinsicElements[T]
    : {};먼저 결론부터 말하면 ButtonHTMLAttributes와 동일하다고 할 수 있지만, 더 짧고 간결하다. 안쓸 이유가 없다.
타입이 조금 복잡한데, 차근차근 따라가보자.
- 첫 번째 조건: T extends JSXElementConstructor<infer P>‘T’가JSXElementConstructor<infer P>에 할당 가능한가?
 즉T가 컴포넌트 생성자 타입이라면, 생성자가 받는 props 타입인P를 반환한다.
- 두 번째 조건: 첫 번째 조건에 만족하지 않고, T extends keyof JSX.IntrinsicElements‘T’가JSX.IntrinsicElements에 할당 가능하면,
 즉T가JSX.IntrinsicElements의 키 중 하나라면,JSX.IntrinsicElements[T]를 반환한다.
React.ComponentProps<'button'> 은 JSX 요소를 생성하는 함수나, 클래스의 타입이 아니므로, 두 번째 조건에 만족한다.
그래서 JSX.IntrinsicElements['button']를 반환한다.
JSX.IntrinsicElements['button']는 또 React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>를 반환한다.
결국 필요한 것들 모두 포함하고 있다.
첫 번째 조건에 부합하는 경우는 다음과 같다.
type Props = { name: string; age: number; };
type Component = (props: Props) => (<div>...</div>);
type ComponentProps = React.ComponentProps<typeof Component>;
// ComponentProps = { name: string; age: number; }이러한 구조로인해 유연하게 사용할 수 있다.
6. React.ComponentPropsWithRef<'button'>, React.ComponentPropsWithoutRef<'button'>
type ComponentPropsWithRef<T extends ElementType> = T extends (new(props: infer P) => Component<any, any>)
    ? PropsWithoutRef<P> & RefAttributes<InstanceType<T>>
    : PropsWithRef<ComponentProps<T>>;
type ComponentPropsWithoutRef<T extends ElementType> = PropsWithoutRef<ComponentProps<T>>;
type PropsWithoutRef<P> =
    P extends any ? ("ref" extends keyof P ? Omit<P, "ref"> : P) : P;
type PropsWithRef<P> =
    "ref" extends keyof P
        ? P extends { ref?: infer R | undefined }
            ? string extends R ? PropsWithoutRef<P> & { ref?: Exclude<R, string> | undefined }
            : P
        : P
        : P;React.ComponentPropsWithRef<'button'>
이것도 타입이 조금 복잡한데, 차근차근 따라가보자.
- 첫 번째 조건: T extends (new(props: infer P) => Component<any, any>)‘T’가new(props: infer P) => Component<any, any>에 할당 가능한가?
 T가 클래스 컴포넌트 생성자에 할당 가능하다면, props 타입에서 ref 를 제외한 나머지 타입과RefAttributes<InstanceType<T>>를 합친 타입을 반환한다.
- 그렇지 않다면, PropsWithRef<ComponentProps<T>>를 반환한다.
PropsWithRef는 더 복잡한데, 간단히 정리해보면 ref가 문자열 타입이 아닌 경우에 해당 ref의 타입을 유지하거나 조정하여 반환한다.
결론
React.ComponentProps<'button'>을 사용하면 간결하고 유연하게 사용할 수 있다.