I have a class:
class A {
a: string = '123'
}
When I do plainToClass(A, {}) it returns me an object where a is undefined.
What I expect is to be able to provide default value to properties. It can be whatever syntax, not necessary the one that I shown. E.g if I do just new A() then I will have the desired a === '123'.
Maybe there is some decorator or other way that I'm missing?
IMO, this is something that should happen by default. If there's a default value set on the target class, don't override it with undefined. This is standard behaviour in serialisation libraries I've worked with in the past.
In the meantime, you could work around it with the Transform decorator.
class A {
@Transform(value => value || '123', { toClassOnly: true })
a: string
}
@guscastro Thanks for the workaround, I think it will work for my case!
I agree that what I described is kind of default behaviour that everyone would expect. So, would be nice to maybe have a specific decorator for that, e.g
@Default('123')
a: string
Came also to realisation there wasn't a decorator for this.
@pleerock I agree with @kirillgroshkov that would be a nice to have.
Could we reopen this ticket please?
Yeah, we can have a @Default decorator. This needs co-op with class-validator.
Is the workaround given by @guscastro supposed to work ?
model.a is still undefined on my side.
const json = {};
class A {
@Transform(value => value || "123", { toClassOnly: true })
a: string;
}
model = plainToClass(A, json);
What am I missing ?
Ok, it seems that I have to add the @Exposed decorator to the field for it to be picked as key.
Is it supposed to be the default behavior ?
Any update on this one? Would be lovely to have @Default decorator...
Here is the decorator I'm using:
import { Transform } from 'class-transformer';
export function Default(defaultValue: any) {
return Transform((value: any) => (value !== null && value !== undefined ? value : defaultValue));
}
The solution should be as @guscastro noted:
IMO, this is something that should happen by default. If there's a default value set on the target class, don't override it with
undefined.
Vote for this. There's a corresponding issue in class-validator (https://github.com/typestack/class-validator/issues/177)
@jbdemonte 's solution has an issue when used with non-simple data types.
For example if using @Default([]) to initiate an array, all instances would share the same array.
To prevent this, I extended his solution to (flatly) copy array and object values, or to use a function returning some value.
export function Default(defaultValue: any) {
return Transform((value: any) => {
if (value !== null && value !== undefined) return value;
if(typeof defaultValue==='function') return defaultValue();
if(Array.isArray(defaultValue)) return [...defaultValue];
if(typeof defaultValue==='object') {
return (defaultValue===null) ? null : {...defaultValue};
}
return defaultValue;
});
}
@Default([])
@Default({})
@Default(()=>return {...data...} as SomeComplexDefaultValue )
If you already have lodash installed, @jbjhjm's answer can be written as:
import { Transform } from 'class-transformer'
import { cloneDeep } from 'lodash'
export function Default(defaultValue: unknown): PropertyDecorator {
return Transform((value: unknown) => value ?? cloneDeep(defaultValue))
}
There is some related work in #381.
I think it should work without any decorators, because we have in js/ts everything for this. IMHO this is a bug
The feature has been merged, will be released with the next version.
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Most helpful comment
IMO, this is something that should happen by default. If there's a default value set on the target class, don't override it with
undefined. This is standard behaviour in serialisation libraries I've worked with in the past.In the meantime, you could work around it with the
Transformdecorator.