The use case here is for instance mapping a fetch response with a json result into a more complex class using generics.
For instance mapper<T,U>(U json) where T is the expected return Type. And U is optionally the json class.
U could be an array e.g. resulting in T[] or Array<T> being mapped out.
For instance given a User:
interface CustomAdressImplementation {
street: string;
streetNumber: string;
city: string;
zip: string;
}
interface User {
id: string;
birthDate: Date;
name: string;
address: CustomAdressImplementation;
}
example json UserJson:
[
{
"id": 1,
"birthdate": "2020-02-20T10:00:19.145Z"
"name": "Leanne Graham"
"address": {
"street": "Kulas Light",
"streetNo": "2b"
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
},
[... more users]
]
One way of custom mapping this is just by creating a mapper method:
interface GeoJson {
lat: string;
lng: string;
}
interface AddressJson {
street: string;
streetNo: string;
suite: string;
city: string;
zipcode: string;
geo: GeoJson;
}
interface UserJson {
id: number;
birthdate: string;
name: string;
address: AddressJson;
}
export function mapAddress(json: AddressJson): CustomAdressImplementation {
return {
street: json.street,
streetNumber: json.streetNo,
city: json.city,
zip: json.zipcode,
};
}
export function mapUser(json: UserJson): User {
return {
id: json.id.toString(),
birthDate: new Date(json.birthdate),
name: json.name,
address: mapAddress(json.address),
};
}
export function mapUserList(json: UserJson[]): User[] {
if (json) return json.map(mapUser);
return [];
}
Is there a more elegant way of creating a ruleset for mapping this using generics? Or is manual custom mapping the way to go in typescript trading of complex mapping logic.
In my example Date, string conversion and mapping to the address class seems like it could be written more elegantly avoiding the boilerplate custom mapper method.
Using the class-transformer library is not an accepted solution, looking for a pure Typescript implementation using generics.
AutoMapper TypeScript is a tool that provides object-object mapping by convention and it follows as close as possible to the original .NET AutoMapper.
The example here is quite similar to the problem description and also provides a solution.
You can create a mapList function that inference the passed mapper:
// Some UserJson type
type UserJson = {
[key: string]: any;
};
// User type
type User = {
id: string;
birthDate: Date;
name: string;
orderLines: any;
};
// All mappers should receive a json of type T and return a type U
interface Mapper<T, U> {
(json: T): U;
}
// We declare the mapper of user, should receive a UserJson (T) and returns an User (U)
type mapUser = Mapper<UserJson, User>;
function mapUser(json: UserJson): User {
return {
id: json.id.toString(),
birthDate: new Date(json.birthdate),
name: json.name,
orderLines: json.address
};
}
// mapList receives a Mapper<T, U> and returns a function with type (json: T[]) => U[]
function mapList<T, U>(mapper: Mapper<T, U>): (json: T[]) => U[] {
return (json: T[]): U[] => {
if (json) return json.map(mapper);
return [];
};
}
// As mapList received a Mapper<UserJson, User>, it will return (json: UserJson[]) => User[]
const mapUserList = mapList(mapUser);
Demo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With