Home > Net >  Instantiate a generic type with every key of a different type
Instantiate a generic type with every key of a different type

Time:05-04

I have a working 'solution' but it's very ugly and I'm wondering if there's a cleaner one. The code I have written is something along the lines of the following, this accomplishes my goal of getting typescript to correctly infer the type of value in each of the branches of the switch statment.

type Foo = {
  key1: string
  key2: number
  key3: {message: string}
}

type FooProperty<Key extends keyof Foo> = {
  key: Key,
  value: Foo[Key],
}

type FooPropertyData = FooProperty<'key1'> | FooProperty<'key2'> | FooProperty<'key3'>

const func = (change: FooPropertyData) => {
  switch (change.key) {
    case "key1": {
      return change.value.toUpperCase()
    }
    case "key2": {
      return change.value * 8
    }
    case "key3": {
      return change.value.message
    }
  }
}

However this requires you to manually add every key of Foo to the FooPropertyData type. which is clearly not ideal. My first thought would be to do something like:

type FooPropertyData = FooProperty<keyof Foo>

But this loses type safety. Is there a better way to do this?

Apologies if the title is misleading I'm new to generic types and am not exactly sure how best to word it.

CodePudding user response:

The solution is to make TypeScript use each constituent of keyof Foo separately:

type FooPropertyData<T extends keyof Foo = keyof Foo> = T extends T ? FooProperty<T> : never;

This phenomenon can be demonstrated in the following:

type All<T> = Set<T>;

type Each<T> = T extends T ? Set<T> : never;

type AllIn = All<"1" | "2">; // Set<"1" | "2">

type EachIn = Each<"1" | "2">; // Set<"1"> | Set<"2">

Playground

  • Related