1

I want to do some type mapping like this:

There are some modules with id and actions, I want to combine all actions method and remap its method name adding ${id}/ prefix, sample code is here:

const m1 = {
  id: 'm1',
  actions: {
    down(id: string) {
      return true;
    },

    up(n: number) {
      return 0;
    }
  }
}

const m2 = {    
  id: 'm2',
  actions: {
    play(id: string) {
      return true;
    },

    go(n: number) {
      return 0;
    }
  }
}

type MyModule = ??

// should return :
// MyModule = {
//   'm1/down': (id: string) => boolean,
//   'm1/up': (n: number) => number;
//   'm2/play': (id: string) => boolean;
//   'm2/go': (n: number) => number;
// }

Playground

Is this possible in typescript?

Sky
  • 39
  • 7

1 Answers1

1

You can do this using mapped types and template literal types in newer versions of Typescript:

type ModuleDefnition = {
  id: string,
  actions: Record<string, (...a: any[]) => any>
}
type Module<T extends ModuleDefnition> = {} & {
  [P in keyof T['actions'] & string as `${T['id']}/${P}`]: T['actions'][P]
}

type MyModule = Module<typeof m1> & Module<typeof m2>

Playground Link

The only change to the module definitions is that for id you need to make sure you have a string literal type

You can also use UnionToIntersection (from here) if you have an unknown number of modules or you want to create the modules using a function.


type DistributeModule<T extends ModuleDefnition> = T extends T ? Module<T> : never;

type UnionToIntersection<U> = 
  (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never

function merge<T extends ModuleDefnition[]>(...a: T):UnionToIntersection<DistributeModule<T[number]>> {
  return null!;
}

let myModule = merge(m1, m2)

Playground Link

Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357
  • 1
    Awesome answer! I tried `[P in \`${T['id']}/keyof T['actions']\`]: T['actions'][P]` and stucked here, never thought can use `as` here. And for the `const` id, `UnionToIntersection`, really resolved my problem, learned a lot from your answer, really thx :) – Sky Feb 21 '22 at 06:36