UNPKG

@modern-kit/react

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