class-transformer
class-transformer copied to clipboard
feature: extends discriminator lookup
Description
I truly appreciate the possibility to have pseudo dynamic class in my models. Unfortunately, it's not always possible to add a new specific property to make the lookup working and moreover quite often the discriminator can already be found in the parent object.
export class SignTransaction {
@Type(() => Identity)
@ValidateNested()
identity: Identity;
@Type(() => LedgerTransaction, {
discriminator: {
property: ['identity', 'coinType'],
parentProperty: true,
subTypes: [
{ value: BigChainTransaction, name: 822 },
{ value: EthereumTransaction, name: 60 },
],
},
keepDiscriminatorProperty: true,
})
@ValidateNested()
transaction: BigChainTransaction | EthereumTransaction;
}
Proposed solution
- Allow to lookup discriminator in parent value
- Allow search through nested properties
- Allow subTypes.name to be string or number
Relates to :
- #737
- #582
- #552
- #510
Tip: While that solutions are not implemented I'm using the generic @Transform decorator to convert to the specific classes. Example with top-level param:
export abstract class Photo {
id: number;
filename: string;
}
export class Landscape extends Photo {
panorama: boolean;
}
export class Portrait extends Photo {
person: Person;
}
export class UnderWater extends Photo {
depth: number;
}
enum TopPhotoType {
'landscape' = 'landscape',
'portrait' = 'portrait',
'underwater' = 'underwater'
}
const topPhotoTypeMap: { [key in TopPhotoType]: new () => Photo } = {
'landscape': Landscape,
'portrait': Portrait,
'underwater': UnderWater
}
export class Album {
id: number;
name: string;
topPhotoType: TopPhotoType;
@Transform(
({ value, obj }) => {
const specificClass = topPhotoTypeMap[obj.topPhotoType];
return plainToInstance(specificClass, value);
},
{
toClassOnly: true
}
)
@Type(() => Object)
topPhoto: Landscape | Portrait | UnderWater;
}
let album = plainToClass(Album, albumJson);
To make @jonasof solution even more useful i suggest to also resolve the dynamic type so that @ValidateNested can also be used. To continue with his example:
export abstract class Photo {
id: number;
filename: string;
}
export class Landscape extends Photo {
panorama: boolean;
}
export class Portrait extends Photo {
person: Person;
}
export class UnderWater extends Photo {
depth: number;
}
enum TopPhotoType {
'landscape' = 'landscape',
'portrait' = 'portrait',
'underwater' = 'underwater'
}
const topPhotoTypeMap: { [key in TopPhotoType]: new () => Photo } = {
'landscape': Landscape,
'portrait': Portrait,
'underwater': UnderWater
}
export class Album {
id: number;
name: string;
topPhotoType: TopPhotoType;
@Transform(
({ value, obj }) => {
const specificClass = topPhotoTypeMap[obj.topPhotoType];
return plainToInstance(specificClass, value);
},
{
toClassOnly: true
}
)
@Type(({ object }) => topPhotoTypeMap[object?.topPhotoType])
@ValidateNested()
topPhoto: Landscape | Portrait | UnderWater;
}
let album = plainToClass(Album, albumJson);