Generisk mappare med Typescript
Användningsfallet här är till exempel att mappa ett fetch-svar med ett JSON-resultat till en mer komplex klass med hjälp av generiska typer.
Till exempel mapper<T,U>(U json) där T är den förväntade returtypen. Och U är valfritt json-klassen.
U kan vara en array t.ex. vilket resulterar i att T[] eller Array<T> mappas ut.
Till exempel givet en User:
interface CustomAdressImplementation {
street: string;
streetNumber: string;
city: string;
zip: string;
}
interface User {
id: string;
birthDate: Date;
name: string;
address: CustomAdressImplementation;
}
exempel på 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]
]
Ett sätt att anpassa mappningen på detta är helt enkelt genom att skapa en mappningsmetod:
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 [];
}
Finns det ett mer elegant sätt att skapa ett regelset för att mappa detta med hjälp av generics? Eller är manuell anpassad mappning vägen att gå i TypeScript när man hanterar komplex mappningslogik.
I mitt exempel verkar datum-, strängkonvertering och mappning till adressklassen kunna skrivas mer elegant och undvika den rutinmässiga anpassade mappningsmetoden.
Att använda class-transformer-biblioteket är inte en accepterad lösning, letar efter en ren Typescript-implementation som använder generics.
Svar #1
AutoMapper TypeScript är ett verktyg som tillhandahåller objekt-till-objekt-typmappning enligt konvention och följer så nära som möjligt det ursprungliga .NET AutoMapper.
Exemplet här är ganska likt problemets beskrivning och ger också en lösning.
Svar #2
Du kan skapa en mapList-funktion som härleder den skickade mappningen:
// 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
