Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Discriminatable key-value tuple type (of a given object)

Given the following code:

interface Obj {
  foo: string;
  bar: number;
}
type ObjKey = keyof Obj;
type ObjVal<Key extends ObjKey> = Obj[Key];
type ObjTuple<Key extends ObjKey> = [Key, ObjVal<Key>];

I want to create a function that takes in a tuple (of unknown type), and discriminates the Key part (at index 0), to figure out what the value type is:

function doThings(tuple: ObjTuple): string {
  switch (tuple[0]) {
    case "bar":
      return tuple[1].toFixed();
    case "foo":
      return tuple[1];
  }
}

Of course, this doesn’t work, as the type argument for ObjTuple is missing. Since I don’t want to specify it (the function should figure out what type it is), I changed the declaration to have ObjKey as default:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

type ObjTuple<Key extends ObjKey = ObjKey> = [Key, ObjVal<Key>];

Sadly, the discrimination is not working at all. TypeScript correctly detects the allowed variants of the key (e.g. tells me when a code path is missing, or when the switch contains an impossible key), but the value is always a union of all the variants.

Here is a link to a playground, with the final code.

>Solution :

The problem here is the type of ObjVal. The editor is not doing us a favor when displaying it but here is how the expanded type looks like:

type ObjTuple = ["foo" | "bar", string | number]

That does not look right at all since there is not correlation between specific properties and types anymore. Both elements are just a union.

We rather want a type which looks like this:

type ObjTuple = ["foo", string] | ["bar", number]

And we can generate this one using ObjTuple with one modification. We need to distribute over Key.

type ObjTuple<Key extends ObjKey = ObjKey> = Key extends Key 
  ? [Key, ObjVal<Key>]
  : never

This should solve the issue.


Playground

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading