I have an object of two Domains
plus three Events
per Domain
. Now I want to pick one Event
and its resp. Domain
. I get the following error.
Type 'string' is not assignable to type 'never'.ts(2322)
Untitled-1(8, 5): The expected type comes from property 'Event' which is declared here on type
'Group<{ Domain0: Enum<"Event0_0" | "Event0_1" | "Event0_2">; Domain1: Enum<"Event1_0" | "Event1_1" | "Event1_2">; },
"Domain0" | "Domain1">'
If I have only one Domain
, everything is working fine, so I assume, TS takes some kind of intersection, thus type never
.
type Enum<K extends PropertyKey> = {
[key in K]: key;
};
type Events = Record<string, Enum<string>>;
interface Group<events extends Events, domain extends keyof events> {
Domain: domain;
Event: keyof events[domain];
};
interface Obj<events extends Events> {
Group: Group<events, keyof events>;
};
const obj: Obj<{
Domain0: Enum<"Event0_0" | "Event0_1" | "Event0_2">,
Domain1: Enum<"Event1_0" | "Event1_1" | "Event1_2">,
}> = {
Group: { Domain: "Domain0", Event: "Event0_0" },
};
Enum<"a" | "b">
simply is an object of shape
{
a = "a",
b = "b",
}
CodePudding user response:
Let's go through this step-by-step.
You are passing the following object into Obj
as events
.
{
Domain0: Enum<"Event0_0" | "Event0_1" | "Event0_2">,
Domain1: Enum<"Event1_0" | "Event1_1" | "Event1_2">,
}
This object type and its keys are passed to Group
. The type of domain
is keyof events
which evaluates to:
"Domain0" | "Domain1"
For the Event
property, you compute keyof events[domain]
. Fully expanded, this would look like this:
keyof {
Domain0: Enum<"Event0_0" | "Event0_1" | "Event0_2">,
Domain1: Enum<"Event1_0" | "Event1_1" | "Event1_2">,
}["Domain0" | "Domain1"]
events[domain]
would evaluate to
Enum<"Event0_0" | "Event0_1" | "Event0_2">
| Enum<"Event1_0" | "Event1_1" | "Event1_2">
which is a union of two object types which do not share any properties. Calling keyof
on this union therefore produces never
.
keyof (
| {
Event0_0: "Event0_0";
Event0_1: "Event0_1";
Event0_2: "Event0_2";
}
| {
Event1_0: "Event1_0";
Event1_1: "Event1_1";
Event1_2: "Event1_2";
}
)
// -> never
To achieve your initial goal, you have to compute a union of valid Domain
/Event
combinations.
type Group<E extends Events> = {
[K in keyof E]: {
Domain: K;
Event: keyof E[K];
}
}[keyof E]
interface Obj<E extends Events> {
Group: Group<E>;
}
Which leads to the following result:
let obj: Obj<{
Domain0: Enum<"Event0_0" | "Event0_1" | "Event0_2">,
Domain1: Enum<"Event1_0" | "Event1_1" | "Event1_2">,
}>
obj = {
Group: { Domain: "Domain0", Event: "Event0_0" },
};
obj = {
Group: { Domain: "Domain0", Event: "Event1_0" },
// ^^^^^ Type '"Domain0"' is not assignable to type '"Domain1"
};