I want to achieve something like this so that the type of the Component
can be inferred and so the props
can be automatically safely typed.
type ComponentsType<T extends React.FC<any>> {
[key: string]: {
Component: T;
props?: React.ComponentProps<T>
};
}
const components: ComponentsType = {
RedButton: {
Component: Button,
props: { // <- this should be automatically typed to take props of Button
color: 'red'
}
},
BlueButton: {
Component: Button,
props: { // <- this should be automatically typed to take props of Button
color: 'blue'
}
},
FullWidthCard: {
Component: Card,
props: { // <- this should be automatically typed to take props of Card
variant: 'full-width'
}
},
}
But the above typing won't work because the generic part is on the root of the object AKA components
, not on each property AKA RedButton
or FullWidthCard
.
So is there a way for typescript for run through them one by one and infer the Component one by one?
Thanks
In order to achieve expected behavior, you need to infer each component and props from function arguments. I mean, you need to create an extra function.
import React, { FC, ComponentProps } from 'react'
const Button: FC<{ color: string }> = (props) => null
const Card: FC<{ variant: string }> = (props) => null
type WithProps<T> =
T extends Record<infer Key, infer _> ? {
[P in Key]: T[Key] extends { Component: infer Comp, props: infer PassedProps }
? Comp extends React.JSXElementConstructor<infer Prps>
? { Component: Comp, props: ComponentProps<Comp> & Record<Exclude<keyof PassedProps, keyof Prps>, never> }
: never
: never
} : never
const components = <
Comp extends React.JSXElementConstructor<any>,
Item extends { Component: Comp },
Components extends Record<string, Item>>(dictionary: Components & WithProps<Components>) => {}
Comp
- infered component
Item
- infered dictionary item, component&props
object
Components
- infered whole argument.
WithProps
- iterates through each object of passed argument and checks whether props
obhect is assignable to component props. If yes - return props, otherwise negate all invalid props to never
.
components({
RedButton: {
Component: Button,
props: {
color: 'red'
}
},
BlueButton: {
Component: Button,
props: {
extra_prop:42, // DRAWBACK, no error
color: 'blue'
}
},
FullWidthCard: {
Component: Card,
props: {
variant_invalid_prop: 'full-width' // expected error
}
},
})
But there is a drawback, this function will accept extra props, like extra_prop
.
PS I hope somebody will come up with better solution where extra props will be invalid also
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.