I have a problem with typings in my code - union type constraint is generated instead of picking only one type from ItemProperties
types dictionary. Is there any solution to make strict constraint for props
property inside ItemConfig
based on current ItemType
?
P. S. wrapping T
generic with tuple at props
type declaration (as said here) not solves the problem.
Simplified code example:
enum ItemType {
Dog = 1,
Car = 2,
Building = 3,
}
interface ItemProperties {
[Item.Dog]: {
name: string;
};
[Item.Car]: {
power: number;
};
}
interface ItemConfig<T extends ItemType = ItemType> {
type: T;
props: T extends keyof ItemProperties ? ItemProperties[T] : {};
}
const config: ItemConfig[] = [
{
type: ItemType.Dog,
props: ...
}
];
Expected typeof props
:
{ name: string }
Actual typeof props
:
{ name: string } | { power: number } | {}
As remarked in the comments, the issue is that
ItemConfig
is equal to{type: ItemType, props: {} | {name: string} | {power: number}}
whereas you'll want it to be a discriminating union{type: ItemType.Dog, props: {name: string}} | {type: ItemType.Car, props: {power: number}} | ..
to type the array elementprops
correctly.One way to create this union is by using a distributive conditional type (docs):
TypeScript playground
Because the
T
in the condition is a naked type parameter,ItemConfig<ItemType.Dog | ItemType.Car | ItemType.Building>
distributes toItemConfig<ItemType.Dog> | ItemConfig<ItemType.Car> | ItemConfig<ItemType.Building>
, which is the desired union.Alternatively (as captain-yossarian remarked), since
ItemType
extendsPropertyKey
(i.e.string | number | symbol
), you can use a mapped type to create an object that has the constituents of the desired union as its values, and index that object to obtain the union:TypeScript playground
This has the advantage that you don't need
ItemConfig
to have a generic parameterT
, but it is limited to types that extendPropertyKey
(otherwise you can't use it as a key in the mapped type).Both approaches approaches yield the same
ItemConfig
union, which will allow the appropriateprops
type to be inferred for each array element: