Search code examples
typescripttuplestemplate-literalsmapped-types

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


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} }.


Solution

  • 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