π 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
https://www.typescriptlang.org/play/?jsx=4&ts=5.6.2#code/JYWwDg9gTgLgBAbwLACg5xgTzAUzgUQBscQcA7GAFWxwBpV0tc4BhCcCM8mABSgjABnemgw1W7SFwp8BggOrAYACwgBXGACUcAMxGNxbDtKo19cHdADuAQygATbTrg3BcAEaucAMWt3HuuZMeL5Qtg5O2mT2OFDeamQAxjDAnEHiTiIAvhb8IHAA5FA4NskFANyoqDoJyalkFn4RugA8lLRwPAB8ABQMcImSnNwAXHA9YPxCYzwdxTpjTm1dAJRwALxdcNqlMAB0O8kAchAxIitjE1OCM3AAZIjzAPyLrZRdWWub2yXJB78wE4xRD9YowNRQBqeQQ+JoBHR9UToQbGbguNwJADWZAgVgarjgoXC8KiMTitRSnBaWJxeI6NNxZC65hWlRQWSqKGCElRFEUKiJ-icsiEbTgOAAHjByPY3EQSNxqLgtut+kYpNwRQolKoNEt3vcQUiBoRXIIjjZSC84IIYFBgGQAOZs9DoVzWyhwAA+cASMR0Dpw9hdcCybNQgzItp5Gr5OsFzWc60aYSFukR6DFkul0TlxFIFCVeGTBUEYBsZAKvX66EmcjG6uGcYFcOF12W5nQ81eOhaAAlKABZAAy8oLMGZ-S+W2QxrBEIaZDUhEIIay53DXPElBwtuWGzgnuzMrcCBRsZg1odOliMabMA5oied5M4qlJ5fipoLQrmC6NbgZ9GxMLUWmA7h-2NMYuAAN1ifpoJwOCoE3bltEEZcYAARgPcCZHbYIIGcPCYH5ZQE3hLpyjgAB6GiMGUPBigwwh4GANwrBwYAHGoqxlEwOAAAMAHkQCUH8yEwDoil0KtBKeVA0N3TCACYDx3PczyGEwxkI4jtO4MiKKcLIqNo+iVCY5TWLgdiBmgYpkjGc971LOBoGAR0HRsQg4DrIQ4AAajgeY-KmPZUCAA
π» 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
π Search Terms
react, forwardRef, component props, ComponentProps
π Version & Regression Information
β― Playground Link
https://www.typescriptlang.org/play/?jsx=4&ts=5.6.2#code/JYWwDg9gTgLgBAbwLACg5xgTzAUzgUQBscQcA7GAFWxwBpV0tc4BhCcCM8mABSgjABnemgw1W7SFwp8BggOrAYACwgBXGACUcAMxGNxbDtKo19cHdADuAQygATbTrg3BcAEaucAMWt3HuuZMeL5Qtg5O2mT2OFDeamQAxjDAnEHiTiIAvhb8IHAA5FA4NskFANyoqDoJyalkFn4RugA8lLRwPAB8ABQMcImSnNwAXHA9YPxCYzwdxTpjTm1dAJRwALxdcNqlMAB0O8kAchAxIitjE1OCM3AAZIjzAPyLrZRdWWub2yXJB78wE4xRD9YowNRQBqeQQ+JoBHR9UToQbGbguNwJADWZAgVgarjgoXC8KiMTitRSnBaWJxeI6NNxZC65hWlRQWSqKGCElRFEUKiJ-icsiEbTgOAAHjByPY3EQSNxqLgtut+kYpNwRQolKoNEt3vcQUiBoRXIIjjZSC84IIYFBgGQAOZs9DoVzWyhwAA+cASMR0Dpw9hdcCybNQgzItp5Gr5OsFzWc60aYSFukR6DFkul0TlxFIFCVeGTBUEYBsZAKvX66EmcjG6uGcYFcOF12W5nQ81eOhaAAlKABZAAy8oLMGZ-S+W2QxrBEIaZDUhEIIay53DXPElBwtuWGzgnuzMrcCBRsZg1odOliMabMA5oied5M4qlJ5fipoLQrmC6NbgZ9GxMLUWmA7h-2NMYuAAN1ifpoJwOCoE3bltEEZcYAARgPcCZHbYIIGcPCYH5ZQE3hLpyjgAB6GiMGUPBigwwh4GANwrBwYAHGoqxlEwOAAAMAHkQCUH8yEwDoil0KtBKeVA0N3TCACYDx3PczyGEwxkI4jtO4MiKKcLIqNo+iVCY5TWLgdiBmgYpkjGc971LOBoGAR0HRsQg4DrIQ4AAajgeY-KmPZUCAA
π» 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:
π Actual behavior
Type
Result1is wrong:and
Result2is correct:π Expected behavior
Types
Result1andResult2are same:Additional information about the issue
No response