type ComponentType = (...args: any) => any;
type PlatformNotificationProps<TIcon extends ComponentType = ComponentType> = {
component: TIcon;
arg: Parameters<TIcon>[0];
};
const PlatformNotification = (props: PlatformNotificationProps) => {};
const Icon = (name: string) => '';
const result = PlatformNotification({
component: Icon,
arg: 100,
});
In this case or 'arg' is incorrect and should be a string, or component is incorrect and should accept number instead string. I expecting to see error in console, but everything is ok.
How I can write types for this case?
CodePudding user response:
When using generics, all types along the chain need to pass over the generic arguments.
Since you are defaulting the arguments in PlatformNotificationProps
and then using that type without configuring it, TS will not know to associate the arguments from your function to those of the generic
Here's one way you can do it by having all elements in your chain to be configurable
type Fn<Args extends any[] = any[]> = (...args: Args) => any;
type PlatformNotificationProps<Args extends any[], TIcon extends Fn<Args>> = {
component: TIcon;
arg: Parameters<TIcon>[0];
};
const PlatformNotification = <Args extends any[] = any[], Comp extends Fn<Args> = Fn<Args> >(props: PlatformNotificationProps<Args, Comp>) => {};
const Icon = (name: string) => '';
const result = PlatformNotification({
component: Icon,
arg: 100,
});
CodePudding user response:
The major problem is that you are assigning a default generic to PlatformNotificationProps
:
type PlatformNotificationProps<TIcon extends ComponentType = ComponentType> = {
...
When you call this type without a proper generic type, typescript can only infer it to be of ComponentType
, this is why arg
can accept numbers, because it is actually typed as any
:
const result = PlatformNotification({
component: Icon,
arg: 100,
// ^? (property) arg: any
});
The same for component
actually:
const result = PlatformNotification({
component: () => null, // no errors
arg: 100,
});
In order to fix this, call PlatformNotificationProps
with a type:
const PlatformNotification = (props: PlatformNotificationProps<typeof Icon>) => {};
And now:
const result = PlatformNotification({
component: Icon,
arg: 100, // error: type `number` is not assignable to `string`
});
CodePudding user response:
You have defined PlatformNotification
as a non-generic function, don't expect it to check types
const PlatformNotification = (props: PlatformNotificationProps) => {};
// ^?
// const PlatformNotification: (props: PlatformNotificationProps</*default*/ComponentType>) => void
const result = PlatformNotification({
component: Icon,
// ^?
// (property) component: ComponentType
arg: 100,
// ^?
// (property) arg: any
});
Just make it generic and it'll work
const PlatformNotification = <TIcon extends ComponentType>(props: PlatformNotificationProps<TIcon>) => {};