typestack / class-transformer

Decorator-based transformation, serialization, and deserialization between objects and classes.
MIT License
6.66k stars 487 forks source link

Cannot transform string to number #1672

Open stad-nico opened 6 months ago

stad-nico commented 6 months ago

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"
  }
andylizf commented 3 months ago

same question

diffy0712 commented 3 months ago

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:

I am not sure why it behaves differently in these cases yet.

abouroubi commented 3 months ago

Same error here

diffy0712 commented 3 months ago

To note a few things:

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.

abouroubi commented 3 months ago

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.

diffy0712 commented 3 months ago

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:)

PurpleTape commented 3 months ago

Same with Vite and code:

import 'reflect-metadata';
import { plainToClass } from 'class-transformer';

class Entry {
    num: number;
}

console.log(plainToClass(Entry, { num: '5.00' }));