class-transformer
class-transformer copied to clipboard
Cannot transform string to number
Description
Trying to convert number strings wont cast the type to number. This was supposed to be fixed in 0.2.2
(Issue #179)
Minimal code-snippet showcasing the problem
import { plainToClass } from "class-transformer";
import { IsDate } from "class-validator";
import "reflect-metadata";
export class T {
@IsDate()
number!: number;
}
const m = plainToClass(T, { number: "10" }, { enableImplicitConversion: true });
console.log(m); // -> T { number: "10" }
Expected behavior
console.log(m); // -> T { number: 10 }
Actual behavior
console.log(m); // -> T { number: "10" }
The workaround using @Type(() => Number)
still works, however I expect number strings to be converted without additionally specifying the type.
Using
"dependencies": {
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"reflect-metadata": "^0.2.1",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
}
same question
It seems that there is some problem with the environment and not the class-transformer
package.
The package will try to find the correct type if it has metadata information, which will be available at runtime using the reflect-metadata
package.
In fact when the mentioned pull request was merged it contained tests for checking this behavior and they still pass when I ran them.
If you log the (Reflect as any).getMetadata( "design:type",T.prototype,"number"),
it should have value [Function: Number]
, but it is undefined in some cases and then the package will default to the original type.
I have tested your code:
- in a new
vite
project it did not work - in a vitest test it did not work
- using only npx ts-node it did work for me though
I am not sure why it behaves differently in these cases yet.
Same error here
To note a few things:
-
type declaration is emitted only if some decorator is added to the property.
(so you must add at least one decorator to have type declaration available! - The
emitDecoratorMetadata
in your tsconfig might not have any affect if you are transpiling your typescript code other than tsc (which will result in an empty metadata so reflect-metadata will not be able to say anything about your types) for example:-
Babel in its docs states the following:
-
--emitDecoratorMetadata This option isn't supported by an official Babel package since it is a TypeScript-specific addition and not part of the decorators proposal. - babel docs.
- but you might try babel-plugin-transform-typescript-metadata
-
- Vite uses esbuild by default which, will not treat types in typescript files
-
SWC can be used to emitDecoratorMetadata (I have tested and it worked as expected), but keep in mind that
@vitejs/plugin-react-swc
plugin for Vite does not support emitting decorators.
-
Babel in its docs states the following:
I think there are a few serious limitations to this feature unfortunately, which should be mentioned somewhere.
But in the example provided by @stad-nico I see ts-node
as dependency, which should work. Do you use ts-node to run the example above or something else?
When I tried to run your example with the same dependencies using ts-node it worked as expected.
This issue appeared recently in my code, without any changes in my parts. It looks weird because here is the emitted code:
__decorate([
(0, class_validator_1.IsNumber)(),
__metadata("design:type", Number)
], User.prototype, "AGE", void 0);
But when i use this code:
const validatedUser = plainToClass(User, userJson, {
enableImplicitConversion: true,
});
the AGE property stays as String instead of being transformed to Number.
For the time being I'll add @Type(() => Number)
as a workaround, if I can help with a reproduction repo I'll try to do it.
Could you checkout a commit from before when it supposadely worked and check if there has been any package-lock.json (or yarn, or pnpm whatever you use) updated? It should work if no package has been changed since. what environment does this runs on? browser or server? If you could provide an example which produces the snippet you mentioned it would be very good.
thank you:)
Same with Vite and code:
import 'reflect-metadata';
import { plainToClass } from 'class-transformer';
class Entry {
num: number;
}
console.log(plainToClass(Entry, { num: '5.00' }));