UNPKG

@modern-kit/react

Version:
1 lines 7.76 kB
{"version":3,"file":"index.mjs","sources":["../../../src/utils/polymorphicForwardRef/index.ts"],"sourcesContent":["import {\n forwardRef,\n Fragment,\n type ComponentProps,\n type ComponentPropsWithRef,\n type ElementType,\n type ForwardRefExoticComponent,\n type ForwardRefRenderFunction,\n type ReactElement,\n} from 'react';\n\n/**\n * @description 유니온 타입에서 각각의 타입에 대해 `Omit`을 적용하는 타입입니다.\n *\n * `조건부 타입`을 사용하여 `분배 법칙`처럼 동작합니다.\n *\n * @template T - 분배 대상이 되는 유니온 타입\n * @template K - 제거할 프로퍼티 키\n *\n * @example\n * type Union = { a: string } | { b: number }\n * type Result = DistributiveOmit<Union, 'a'>\n *\n * // 동작 원리와 순서\n * // 1. Result = DistributiveOmit<Union, 'a'>\n * // 2. Result = Omit<{ a: string }, 'a'> | Omit<{ b: number }, 'a'>\n * // 3. Result = {} | { b: number }\n */\ntype DistributiveOmit<T, K extends PropertyKey> = T extends any\n ? Omit<T, K>\n : never;\n\n/**\n * @description 두 타입을 병합하는 타입입니다.\n *\n * @template A - 첫 번째 타입\n * @template B - 두 번째 타입\n *\n * @example\n * type A = { a: string, b: number }\n * type B = { b: string, c: boolean }\n * type Result = Merge<A, B>\n *\n * // 동작 원리와 순서\n * // 1. Result = Merge<A, B>\n * // 2. Result = Omit<A, 'b' | 'c'> & B\n * // 3. Result = { a: string } & B\n * // 4. Result = { a: string, b: string, c: boolean }\n */\ntype Merge<A, B> = Omit<A, keyof B> & B;\n\n/**\n * @description 유니온 타입의 각 구성 요소에 대해 `B` 타입과의 병합을 수행하는 타입입니다.\n *\n * 각 유니온 멤버에서 `B`의 키들을 제거한 후, `B` 타입과 병합합니다.\n *\n * @template A - 병합의 대상이 되는 유니온 타입\n * @template B - 각 유니온 멤버와 병합될 타입\n *\n * @example\n * type A = { a: string, c: boolean } | { b: number }\n * type B = { c: boolean }\n * type Result = DistributiveMerge<A, B>\n *\n * // 동작 원리와 순서\n * // 1. Result = (Omit<{ a: string, c: boolean }, \"c\"> | Omit<{ b: number }, 'c'>) & B;\n * // 2. Result = { a: string } & B | { b: number } & B;\n * // 3. Result = { a: string, c: boolean } | { b: number, c: boolean }\n */\ntype DistributiveMerge<A, B> = DistributiveOmit<A, keyof B> & B;\n\n/**\n * @description 다형성 컴포넌트의 `props`의 타입을 정의하는 타입입니다.\n *\n * `as`를 포함해 고정 `props`와 지정된 요소 타입에 기본적으로 제공하는 `props`를 병합합니다.\n *\n * @template Component - 렌더링할 요소의 타입을 지정합니다. 예를 들어, 'button', 'div' 등 HTML 요소가 될 수 있습니다.\n * @template PermanentProps - 컴포넌트의 고정 `props` 타입입니다.\n * @template ComponentProps - 지정된 요소 타입에 기본적으로 제공하는 `props` 타입입니다. 예를 들어 button 요소의 경우 type, disabled 등이 있습니다.\n *\n * @example\n * interface ButtonProps {\n * variant: 'primary' | 'secondary';\n * size: 'sm' | 'md' | 'lg';\n * }\n *\n * // button 요소로 렌더링될 때의 타입\n * type HtmlButtonProps = AsProps<'button', ButtonProps, ComponentProps<'button'>>\n * // HtmlButtonProps = {\n * // variant: 'primary' | 'secondary';\n * // size: 'sm' | 'md' | 'lg';\n * // as?: 'button';\n * // ... 기타 button의 HTML 속성들\n * // }\n */\ntype AsProps<\n Component extends ElementType,\n PermanentProps extends Record<string, any>,\n ComponentProps extends Record<string, any>\n> = DistributiveMerge<ComponentProps, PermanentProps & { as?: Component }>;\n\n/**\n * @description `ref`를 포함한 다형성 컴포넌트의 함수 시그니처를 정의하는 타입입니다.\n * 하나의 컴포넌트가 여러 HTML 요소로 렌더링될 수 있도록 하며, 각 요소에 맞는 props와 ref를 자동으로 처리합니다.\n *\n * @template Default - 렌더링할 요소의 타입을 지정합니다. 예를 들어, 'button', 'div' 등 HTML 요소가 될 수 있습니다.\n * @template Props - 컴포넌트의 커스텀 `props` 타입입니다.\n * @template OnlyAs - 컴포넌트가 렌더링될 수 있는 요소 타입을 제한합니다. 예를 들어, 'button' | 'a' 를 넣으면 `as`로 'button' | 'a'만 넣을 수 있게 제한됩니다.\n */\ntype PolymorphicWithRef<\n Default extends OnlyAs,\n Props extends Record<string, any>,\n OnlyAs extends ElementType = ElementType\n> = <T extends OnlyAs = Default>(\n props: AsProps<\n T,\n Props,\n T extends ElementType\n ? ComponentPropsWithRef<T>\n : ComponentProps<typeof Fragment>\n >\n) => ReactElement | null;\n\n/**\n * @description `React.forwardRef`를 사용한 다형성 컴포넌트의 전체 타입을 정의합니다.\n * `ForwardRefExoticComponent`와 `PolymorphicWithRef`를 결합합니다.\n *\n * @template Default - 렌더링할 요소의 타입을 지정합니다. 예를 들어, 'button', 'div' 등 HTML 요소가 될 수 있습니다.\n * @template Props - 컴포넌트의 커스텀 `props` 타입입니다.\n * @template OnlyAs - 컴포넌트가 렌더링될 수 있는 요소 타입을 제한합니다. 예를 들어, 'button' | 'a' 를 넣으면 `as`로 'button' | 'a'만 넣을 수 있게 제한됩니다.\n */\ntype PolyForwardComponent<\n Default extends OnlyAs,\n Props extends Record<string, any>,\n OnlyAs extends ElementType = ElementType\n> = Merge<\n ForwardRefExoticComponent<\n Merge<\n Default extends ElementType\n ? ComponentPropsWithRef<Default>\n : ComponentProps<typeof Fragment>,\n Props & { as?: Default }\n >\n >,\n PolymorphicWithRef<Default, Props, OnlyAs>\n>;\n\n/**\n * @description `React.forwardRef`를 위한 다형성 타입 래퍼입니다.\n * 컴포넌트에 다형성과 `ref` 전달 기능을 모두 부여합니다.\n *\n * @template Default - 렌더링할 요소의 타입을 지정합니다. 예를 들어, 'button', 'div' 등 HTML 요소가 될 수 있습니다.\n * @template Props - 컴포넌트의 커스텀 `props` 타입입니다.\n * @template OnlyAs - 컴포넌트가 렌더링될 수 있는 요소 타입을 제한합니다. 예를 들어, 'button' | 'a' 를 넣으면 `as`로 'button' | 'a'만 넣을 수 있게 제한됩니다.\n *\n * @returns 다형성과 `ref` 전달이 가능한 새로운 컴포넌트 타입을 반환합니다.\n */\ntype PolyRefFunction = <\n Default extends OnlyAs,\n Props extends Record<string, any>,\n OnlyAs extends ElementType = ElementType\n>(\n Component: ForwardRefRenderFunction<any, Props & { as?: OnlyAs }>\n) => PolyForwardComponent<Default, Props, OnlyAs>;\n\n/**\n * @description `React.forwardRef`를 다형성 컴포넌트를 위한 타입으로 캐스팅하는 유틸리티입니다.\n * 기존의 `forwardRef`를 `PolyRefFunction` 타입으로 변환하여 다형성과 `ref` 전달을 모두 지원하는 컴포넌트를 생성할 수 있게 합니다.\n *\n * @example\n * // 기본 사용\n * interface ButtonProps {\n * variant: 'primary' | 'secondary';\n * size: 'sm' | 'md' | 'lg';\n * }\n *\n * const Button = polymorphicForwardRef<'button', ButtonProps>((props, ref) => {\n * const Component = props.as ?? 'button';\n * return <Component ref={ref} {...props} />;\n * });\n *\n * @example\n * // OnlyAs로 요소 타입 제한\n * const Button = polymorphicForwardRef<'button', ButtonProps, 'button' | 'a'>((props, ref) => {\n * const Component = props.as ?? 'button';\n * return <Component ref={ref} {...props} />;\n * });\n */\nexport const polymorphicForwardRef = forwardRef as PolyRefFunction;\n"],"names":[],"mappings":";;AA4LO,MAAM,qBAAA,GAAwB;;;;"}