I’m encountering a TypeScript error when using conditional types with React components.
The issue arises when trying to render different types of components based on a type prop and providing the corresponding props for each type.
type PairingCardProps = {
p: string;
};
type SampleCardProps = {
s: string;
};
export const GRID_CARD_COMPONENTS = {
pairing: ({ p }: PairingCardProps) => <div>Pairing Card</div>,
sample: ({ s }: SampleCardProps) => <div>Sample Card</div>,
};
type TypefaceGridCardProps =
| ({ type: "sample" } & SampleCardProps)
| ({ type: "pairing" } & PairingCardProps);
const TypefaceGridCard = ({ type, ...props }: TypefaceGridCardProps) => {
const Card = GRID_CARD_COMPONENTS[type as keyof typeof GRID_CARD_COMPONENTS];
if (!Card) return null;
return <Card {...props} />;
};
The error I encounter is as follows:
Type PairingCardProps is missing the following properties from type 'SampleCardProps': s
Workaround
I work around this issue by type casting but find the object map solution above preferable.
type TypefaceGridCardProps =
| ({
type: "pairing";
} & PairingCardProps)
| ({
type: "sample";
} & SampleCardProps);
const TypefaceGridCard = ({ type, ...props }: TypefaceGridCardProps) => {
switch (type) {
case "sample":
return <SampleCard {...(props as SampleCardProps)} />;
case "pairing":
return <PairingCard {...(props as PairingCardProps)} />;
default:
throw new Error(`Unknown type: ${type}`);
}
};
2
Answers
I think this us due to typescript as typescript compiler is not able to infer the
Card
component in theTypefaceGridCard
component. I also face this type of issue . So this can be solved as :The compiler is unable to handle the "correlated union types" described in ms/TS#30581, however, there is a suggested refactor described in ms/TS#47109, which considers moving to generics.
First, we will need some map type that will hold props of every component:
Now, let’s redefined
GRID_CARD_COMPONENTS
using mapped type with usingTypeMap
as a source for types:Let’s also define the type of the
GRID_CARD_COMPONENTS
which we will need afterwards:We should also create a mapped type for arguments of
TypefaceGridCard
, which will accept an option generic argumentK
that extends the key ofTypeMap
and is defaulted tokeyof TypeMap
. This type will return the props for the givenK
or a union of all possible props:Let’s apply our types to the
TypefaceGridCard
:Strangely, we get an error when we try to invoke the component in JSX format. I’m not sure about the exact reason, but I think it has to do something about React’s internal generic types, which can’t properly handle such cases.
The workaround is to manually type
Card
asFC<TypeMap[K]>
and define another variable for props that is typed asComponentProps<typeof Card>
and has a value ofprops
:Alternatively, you could use
as
assertion instead of creating another variable, but I wouldn’t recommend it, since it is less type-safe:Link to StackBlitz