Open patricknazar opened 5 years ago
even though according to the documentation the above example should work, I was having issues with this too.
Is Vehicle an abstract class that is extended by Car and Train? I was having a similar issue and needed to replace the first argument abstract class with generic Object to make it work;
Like so:
class Wrapper {
@Type( () => Object, {
discriminator: {
property: 'type',
subTypes: [
{value: Car, name: 'car'},
{value: Train, name: 'train'},
],
},
})
vehicle: Car | Train;
}
I'm not having that problem, I am simply asking for the ability to take a plain object and turn it into an instance of a class based on the type
property without needing to use a wrapper class. But yeah Vehicle
is an abstract class. It's not a super important thing but it would be nice.
Hey @patricknazar, I've had a read through the code and can't see any way of using polymorphism directly on the root object being sent to plainToClass()
. It looks like the polymorphism features only work for nested values in an object.
I reckon it would be more appropriate to retitle this issue as a feature request, rather than a question.
I think PR #175 solves this, it's not merged yet though.
This is a year old, any progress? For now I've written my own helper functions such as:
//// Effects
export const EffectTypeOptions: TypeOptions = {
discriminator: {
property: 'type',
subTypes: [
{ value: Confetti, name: 'confetti' },
{ value: Clip, name: 'clip' },
{ value: Sparks, name: 'sparks' },
{ value: Stamp, name: 'stamp' },
{ value: Watermark, name: 'watermark' },
{ value: Toast, name: 'toast' },
]
},
keepDiscriminatorProperty: true,
}
export function plainToEffect(plain: unknown, options?: ClassTransformOptions): Effect {
const type = (plain as any)[EffectTypeOptions.discriminator!.property]
const subType = EffectTypeOptions.discriminator!.subTypes.find(subType => subType.name === type)
if (!subType)
throw new Error(`Could not find class constructor for type '${type}'`)
return plainToClass(subType.value, plain, options)
}
However the logic is just re-expressing what class-transformer
does internally for nested objects. It'd be great if this utility was built in, in a more abstract way.
Would be really nice to have that.
In my case I have a collection of objects of various sub-types that I need to query from a NoSQL db.
Currently I have to create a wrapper for each collection and to also wrap each api call to apply that transformation which makes it cumbersome.
Came up with the following generic approach for now which allows to use plainToClass
with discriminator:
ClassTransformCustom.js
import {Type, plainToClass as plainToClassOriginal, classToPlain} from "class-transformer";
//helper factory function that constructs wrapper at runtime passing base class and discriminator
let transformWrapperFactory = function (baseClass, options){
return class {
@Type(() => baseClass, options)
data
}
}
let plainToClass = function (clazz, obj, options = {}){
let res
if(options.discriminator){
let objWrapper = {data: obj}
let clazzWrapper = transformWrapperFactory(clazz, options)
let resWrapper = plainToClassOriginal(clazzWrapper, objWrapper, options)
res = resWrapper.data
} else {
res = plainToClassOriginal(clazz, obj, options)
}
return res
}
let classToClass = function (obj, options = {}){
let plain = classToPlain(obj)
return plainToClass(obj.constructor, plain, options)
}
export {
plainToClass,
classToPlain,
classToClass,
}
How to use it:
import {plainToClass} from "./ClassTransformCustom.js"
let plainObj = {description: "some description", type: "NOTE"}
let instance = plainToClass(BaseClass, plainObj, {discriminator: yourDiscriminator})
Is it possible for me to have a
plainToClass(Vehicle, v)
call return either aCar
class orTrain
depending on it'stype
property? I know this is similar todiscriminator
in@Type
. I don't like having to create a wrapper class just to harness that ability: