Type for mapping typescript union to fixed values

50 Views Asked by At

If I have a type like the below and I want to map it to some fixed values at compile time, I can do the following:

type MyType = {
  a: string;
  b: string;
}

const MyMapping: { [k in keyof MyType]: number } = {
  a: 3,
  b: 2,
}

type X = typeof MyMapping['a'] // Should be 3

Playground Link

Is there a way I can do the same for a union of existing types?

type MyType = {
  a: string;
  b: string;
}

const MyMapping: /* Magic type here */ = {
  a: 3,
  b: 2,
}

type X = typeof MyMapping['a'] // Should be 3

I realize this probably won't work in the exact form I have up there, but is there some other way to format this to make this happen?

1

There are 1 best solutions below

0
On

First off, we can simplify {[k in keyof MyType] : number} to be just Record<keyof MyType, number> (basically the same type just using the predefined mapped type Record)

If you are using typescript 4.9 or later, you ca use the satisfies operator to constrain the type of the variable while still lettig the compiler infer the type. Coupled with an as const to preserve the literal types, we get the desired result:

const MyMapping = {
  a: 3,
  b: 2,
} as const satisfies Record<keyof MyType, number>

Playground Link

If you are using a version of typescript before 4.9, you can still do this using an identity function to allow inference to happen but also constrain the type of the object literal:

function makeMap<T extends Record<keyof MyType, V>, V extends number>(o: T & Record<Exclude<keyof T, keyof MyType>, never>) {
  return o
}

const MyMapping = makeMap({
  a: 3,
  b: 2,
  c: 1
})

type X = typeof MyMapping['a'] // 3

Playground Link

There are a few tricks to the function:

  1. The V type parameter is used to force TypeScript to infer number literals for the values in the object instead of number
  2. Record<Exclude<keyof T, keyof MyType>, never> is used to prevent excess properties (this is because we don't get excess property checks when we assign to a type parameter)