Open
Description
π Search Terms
react, forwardRef, component props, ComponentProps
π Version & Regression Information
- This changed between versions 5.5.4 and 5.6.2 - but in version 5.5.4 there are other related bugs π€·ββοΈ
β― Playground Link
π» Code
I noticed that when using custom forwardRef function (I use it so my generic components are typed correctly), I get different results in these two situations:
import {
type ElementType,
type ComponentProps,
type ComponentPropsWithoutRef,
type ComponentType,
forwardRef as baseForwardRef,
type ForwardRefRenderFunction,
type Ref,
} from 'react';
// setup code
function forwardRef<T, P>(
component: (props: P, ref: Ref<T>) => React.ReactNode,
): (props: P & {ref?: Ref<T>}) => React.ReactNode {
return baseForwardRef(
component as unknown as ForwardRefRenderFunction<unknown, unknown>,
);
}
type FooProps<T extends ElementType> =
ComponentPropsWithoutRef<T> & {
className?: string;
as?: T | undefined;
};
const Foo = forwardRef(
<T extends ElementType = 'span'>(
props: FooProps<T>,
ref: Ref<HTMLElement>,
) => {
return null;
},
);
type Test<T> = T extends infer Component
? Component extends ComponentType<any>
? ComponentProps<Component>
: never
: never;
type Result1 = ComponentProps<typeof Foo>; // the result is weird: why `Omit<any, 'ref'>`?
type Result2 = Test<typeof Foo>; // the result is correct: Foo's original props + ref prop.
import {
type ElementType,
type ComponentProps,
type ComponentPropsWithoutRef,
type ComponentType,
forwardRef as baseForwardRef,
type ForwardRefRenderFunction,
type Ref,
} from 'react';
function forwardRef<T, P>(
component: (props: P, ref: Ref<T>) => React.ReactNode,
): (props: P & {ref?: Ref<T>}) => React.ReactNode {
return baseForwardRef(
component as unknown as ForwardRefRenderFunction<unknown, unknown>,
);
}
type FooProps<T extends ElementType> =
ComponentPropsWithoutRef<T> & {
className?: string;
as?: T | undefined;
};
const Foo = forwardRef(
<T extends ElementType = 'span'>(
props: FooProps<T>,
ref: Ref<HTMLElement>,
) => {
return null;
},
);
type Test<T> = T extends infer Component
? Component extends ComponentType<any>
? ComponentProps<Component>
: never
: never;
// β οΈ different results
type Result1 = ComponentProps<typeof Foo>; // the result is weird: why `Omit<any, 'ref'>`?
type Result2 = Test<typeof Foo>; // the result is correct: Foo's original props + ref prop.
π Actual behavior
Type Result1
is wrong:
type Result1 = Omit<any, "ref"> & {
className?: string;
as?: ElementType | undefined;
} & {
ref?: Ref<HTMLElement> | undefined;
}
and Result2
is correct:
type Result2 = PropsWithoutRef<ComponentProps<T>> & {
className?: string;
as?: T | undefined;
} & {
ref?: Ref<HTMLElement> | undefined;
}
π Expected behavior
Types Result1
and Result2
are same:
type Result = PropsWithoutRef<ComponentProps<T>> & {
className?: string;
as?: T | undefined;
} & {
ref?: Ref<HTMLElement> | undefined;
}
Additional information about the issue
No response