typescriptdiscriminated-union

Typescript not enforcing discriminated type on array of objects


I've created the following set of types (simplified):

enum Prevals {
  one = 'one',
  two = 'two',
  three = 'three',
}

type PrevalParams = {
  [Prevals.one]: {
    name: string;
    age: number;
  };
  [Prevals.two]: {
    t: 'thing';
  };
  [Prevals.three]: undefined;
}

type PrevalConfig<Id extends Prevals> = {
  id: Id;
  level: 'error' | 'warning';
  params: PrevalParams[Id];
};

Given the above types, I would expect to be able to assign several PrevalConfig objects to an array and have typescript enforce their parameters. However, it seems to interpret the params as the union of all possible params, rather than as the params associated with the specific ID given.

For example, the following should throw an error but doesn't:

const prevals: PrevalConfig<Prevals>[] = [
  {
    id: Prevals.one,
    level: 'warning',
    params: {
      t: 'thing'
    }
  }
]

I believe this is because the type PrevalConfig<Prevals>[] essentially says "an array of PrevalConfig objects where the Id parameter is the union of all possible Prevals." Given that interpretation, it is technically correct that it types the params as "the union of all possible params". However, that's not what I want.

How do I make this work?

(here's a playground with all this pre-loaded)


Solution

  • A colleague of mine just helped me work this one out. I kept saying to myself, "I need PrevalConfig to be the union of all possible preval configs, not a parameterized type," and indeed there's a (somewhat weird) typescript construct for that:

    type PrevalConfig = {
      [Id in Prevals]: {
        id: Id;
        level: 'error' | 'warning';
        params: PrevalParams[Id];
      };
    }[Prevals];
    

    Basically, you create a mapped type of each preval to it's specifically-typed config. Then in the same swoop, you tell typescript to give you the union of all of the values for that mapped type. Thus, you have the union of all possible preval configs :D.

    Thanks, anonymous colleague!