Home > OS >  Typescript multiple type mapping
Typescript multiple type mapping

Time:02-21

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?

CodePudding user response:

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

  • Related