Hi,
I want to make sure that controller DTO output doesn't contain any additional properties. With TS it is quite easy to cast object literal to class type. As a result without additional discrimination logic in DTO class it is quite easy to skip this safe guard when i call class2plain.
with any2plain(ObjectClass, dto) we can get more safe code.
You should always work in your controller with the actual class instances, which makes it necessary to call classToPlain at the end of your controller, so your user gets the right values.
It is what i have to do now, but i have some problems with this approach:
It is a great idea that we can define validation and serialization schemas together with interfaces, but it would be nice to use these schemas with plain objects. For example, class-validator doesn't force instances creation.
On presentation layer i have information about controller return type and can use corresponded serialization schema without forcing instances creation.
I agree, that's definitely not easy to fix at the moment and leads to potential pitfalls when you forgot to cast a object literal. TypeScript is in the progress to support nominal typing to fix this, see https://github.com/Microsoft/TypeScript/issues/202
For the time being I make sure in my code-base I work always with class instances, so something like any2Plain is never needed, but I agree with you that anyToPlain might make sense in some cases where you're unsure (or don't care) what you got as argument.
any2plain(ObjectClass, dto) would be useful when working with mongoose(/typegoose), because when using classToPlain(doc) class-transformer casts the whole mongoose.Document to the output, to prevent this, doc.toJSON / doc.toObject would need to be called, but it would not be an "instance" of the actual class anymore, so such an function would be really useful
Any progress on this?
I don't understand what would be here the desired behavior? Please add some minimal reproducible example of the desired API. I don't know what any2plain(ObjectClass, dto) actually would do.
@NoNameProvided currently, such functions only accept an input and "guess" the class, this issue is an request to add an optional parameter to set the class explicitly
@NoNameProvided currently, such functions only accept an input and "guess" the class, this issue is an request to add an optional parameter to set the class explicitly
I am not sure I understand, you can use plainToClass(target, plain) to convert to a specific type without guessing.
@NoNameProvided isnt plainToClass converting from plain to class? any2plain is meant to convert from "class" (or class like structure) to plain
example being:
having an typegoose class which gets translated into an mongoose schema & model & document
and when calling any function from class-transformer on an document, the output is not what it should be, because it isnt the right class associated with it, so any2plain would be needed to convert from anything to plain with class
So what extra this library can add what cannot be achieved with any deep clone library? Since the source target is unknown we cannot do any verification or transformation. I simply don't see what value we can add here?
So what extra this library can add what cannot be achieved with any deep clone library?
Main value is data sanitization based on schema defined by class.
@NoNameProvided true, but the problem is people use it, if they want to or not (explicitly or implicitly), thanks to / with NestJS or GraphQL (type-graphql)
and for my specific case, people expect that the typegoose class (an "normal typescript class") can be used to transform the document (mongoose document, cannot be an instance of the source class) that gets generated out of that class from mongoose and expect that all the decorators (like Expose) work (or more importantly, that no "wrong conversion" is happening, like mongoose documents get converted into an object with _doc property that contains everything (even hidden stuff) from the document)
The lack of current example makes it harder for me to understand what is the goal here, but I think I start to grasp it, you actually want something like:
@Exclude()
class MyDecoratedClass {
@Expose()
someWantedProperty: string
}
// ignore the class name this is not final :D
unkownValueToPlainWithSchema(MyDecoratedClass, { someWantedProperty: 'want-this', badProperty: 'this-not-so-much' })
// the above call should return { someWantedProperty: 'want-this' }
So basically you want to use an existing decorated class definition to transform a plain object or class instance to plain object conforming the class definition.
This is still the same as using classToPlain(plainToClass(MyDecoratedClass, input)), no?
or more importantly, that no "wrong conversion" is happening, like mongoose documents get converted into an object with _doc property that contains everything (even hidden stuff) from the document)
I don't know how mongoose work as I have never used it, but shouldn't it work with Exclude?
@Exclude()
class YourMongoClass {
@Expose()
someWantedProperty: string;
}
This way no other property will be converted from classToPlain so _doc is ignored as well. You can also use excludeExtraneousValues option.
that no "wrong conversion" is happening,
What is a wrong conversion? class-transformer doesn't convert values unless you are explicitly asking for it via @Type, @Transform or enableImplicitConversion option.
So basically you want to use an existing decorated class definition to transform a plain object or class instance to plain object conforming the class definition.
yes
This way no other property will be converted from classToPlain so _doc is ignored as well. You can also use excludeExtraneousValues option.
problem is (from my knowledge), the _doc property is made by class-transformer, and dosnt exist in the document
This is still the same as using classToPlain(plainToClass(MyDecoratedClass, input)), no?
wouldnt this mean to convert any->class->plain instead of any->plain (multiple steps / unnecessary extra steps)?
the problem with mongoose & typegoose is:
an "document" is made by Model.create or Model.find (or any query), and is an instance of mongoose.Document, and not an instance of MyClass so no decorators from MyClass are used (old testing and knowing not much about this package - still not knowing much about this package)
problem is (from my knowledge), the _doc property is made by class-transformer, and doesn't exist in the document
That is not possible. class-transformer doesn't just invent new properties.
wouldnt this mean to convert any->class->plain instead of any->plain (multiple steps / unnecessary extra steps)?
Yes, but there are no extra steps. The only extra step is calling new clsType() once all the other operations are required anyway.
an "document" is made by Model.create or Model.find (or any query), and is an instance of mongoose.Document, and not an instance of MyClass so no decorators from MyClass are used (old testing and knowing not much about this package - still not knowing much about this package)
And for this exact solution you can do:
@Exclude() // globally exclude properties
class MyDecoratedClass() {
@Expose()
wantedProperty: string
}
const model = await Model.find(/* find some */);
const sanitizedPlainObject = classToPlain(plainToClass(MyDecoratedClass, model));
That is not possible. class-transformer doesn't just invent new properties.
i know, but testing it before (trying to log / inspect) the property _doc on the source, gives an undefined (or not even listed when using inspect (nodejs util))
Yes, but there are no extra steps. The only extra step is calling new clsType() once all the other operations are required anyway.
im not familiar with this project's structure, so it was just an guess that it would take extra steps
(but i guess discussing the extra property "problem" here would be kinda unrelated to the actual issue/feature request)
Yes, let's discuss the extra property problem in a separate issue if it's still happening.
I am going to close this as wontfix, as the above-commented approach is a trivial solution for this.
```ts
@Exclude() // globally exclude properties
class MyDecoratedClass() {
@Expose()
wantedProperty: string
}
const sanitizedPlainObject = classToPlain(plainToClass(MyDecoratedClass, someObject));
```
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
Any progress on this?