Home > Software engineering >  How can you use Tuple elements as keys in a Mapped Type or template literal in typescript?
How can you use Tuple elements as keys in a Mapped Type or template literal in typescript?

Time:12-29

Are there any incantations to properly type the following function in typescript?

given a function createMap() that takes:

  • a prefix (e.g. foo)
  • and a tuple of suffixes (e.g. ['a', 'b', 'c'])

calling createMap('foo', ['a', 'b', 'c']) return the following mapped type:

  {
    a: 'foo.a',
    b: 'foo.b',
    c: 'foo.c',
  }

The function implementation is pretty straightforward, but the return type doesn't seem to be:

const createMap = <P extends string, S extends string[]>(prefix: P, suffixes: [...S]) => {
  return suffixes.reduce((map, suffix) => ({
    ...map,
    [suffix]: `${prefix}.${suffix}`,
  }), {});
}

I know this is incorrect, but it would naively look something like { [K in S]: ${P}.${K in S} }.

CodePudding user response:

In order to achieve it you must set the list as const

then you can do the following:

type CreateMap = <S extends string, A extends ReadonlyArray<string>>(s: S, a: A) => {
  [K in (A extends ReadonlyArray<infer U> ? U : never)]: `${S}.${K}`
};

const createMap: CreateMap = (s, a) => {
  return Object.fromEntries(a.map(key => [key, `${s}.${key}`])) as any
}

const list = ['a', 'b', 'c'] as const

const res = createMap('foo', list)

You can see an example here

CodePudding user response:

We can use a mapped type to compute the return type of the function. We only need to map over the elements in S by computing the union S[number]. Each element K can now be used to concatenate the prefix P with a template literal type.

const createMap = <
  P extends string, 
  S extends string[]
>(prefix: P, suffixes: [...S]) => {
  return suffixes.reduce((map, suffix) => ({
    ...map,
    [suffix]: `${prefix}.${suffix}`,
  }), {}) as { [K in S[number]]: `${P}.${K}` }
}

const result = createMap('foo', ['a', 'b', 'c'])
//    ^? const result: { a: "foo.a"; b: "foo.b"; c: "foo.c"; }

Playground

  • Related