Home > other >  Type 'string' is not assignable to type 'never'.ts(2322)
Type 'string' is not assignable to type 'never'.ts(2322)

Time:01-04

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"
};

Playground

  • Related