Just lately I had a need for a mapping engine in TypeScript. I came up with the follwing basic design. I unfortunately couldn’t use it as it works with classes only and our API client generation tool returns types only. It might be of some use for those who do need to map classes however. Have fun with it!
export class MapEngine {
private readonly mappers: Map<string, any> = new Map<string, any>();
public get size(): number {
return this.mappers.size;
}
public map<TSource, TDestination>(
sourceType: new (...args: any[]) => TSource,
destinationType: new (...args: any[]) => TDestination,
source: TSource ): TDestination {
const key = this.createKey(sourceType, destinationType);
if(!this.mappers.has(key)) {
throw new Error(`No map defined for: ${key}`);
}
const mapper = this.mappers.get(key) as (source: TSource) => TDestination;
const result = mapper(source);
return result;
}
public set<TSource,TDestination>(
sourceType: new (...args: any[]) => TSource,
destinationType: new (...args: any[]) => TDestination,
map: (source: TSource) => TDestination): MapEngine {
const key = this.createKey(sourceType, destinationType);
this.mappers.set(key, map);
return this;
}
public get<TSource,TDestination>(
sourceType: new (...args: any[]) => TSource,
destinationType: new (...args: any[]) => TDestination): (source: TSource) => TDestination {
const key = this.createKey(sourceType, destinationType);
const result = this.mappers.get(key) as (source: TSource) => TDestination;
return result;
}
public has<TSource,TDestination>(
sourceType: new (...args: any[]) => TSource,
destinationType: new (...args: any[]) => TDestination): boolean {
const key = this.createKey(sourceType, destinationType);
const result = this.mappers.has(key);
return result;
}
public delete<TSource,TDestination>(
sourceType: new (...args: any[]) => TSource,
destinationType: new (...args: any[]) => TDestination): boolean {
const key = this.createKey(sourceType, destinationType);
const result = this.mappers.delete(key);
return result;
}
public clear(): void {
this.mappers.clear();
}
private createKey<TSource, TDestination>(
sourceType: new (...args: any[]) => TSource,
destinationType: new (...args: any[]) => TDestination): string {
const result = `${sourceType.name} -> ${destinationType.name}`;
return result;
}
}
It can be used like this:
let mapEngine = new MapEngine();
mapEngine.set(Number, String, (source)=> source.toString());
mapEngine.set(Date, String, (source)=> source.toLocaleString());
mapEngine.set(Date, Number, (source)=> source.getTime());
let mappedResult1 = mapEngine.map(Number, String, 15);
console.log(mappedResult1);
let mappedResult2 = mapEngine.map(Date, String, new Date());
console.log(mappedResult2);
let mappedResult3 = mapEngine.map(Date, Number, new Date());
console.log(mappedResult3 );