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;
// }
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>
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)