typestack / class-transformer

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

Class transformer change the shape of response in typescript #1718

Closed vahidvdn closed 2 months ago

vahidvdn commented 3 months ago

I'm using class transformer to change the shape of my response in my typescript application. This is my initial structure:

[
  {
    "languages": [
      {
        "language_shortcode": "de",
        "label": "ASP-Einrichtung"
      },
      {
        "language_shortcode": "en",
        "label": "Some text"
      }
    ]
  },
  {
    "languages": [
      {
        "language_shortcode": "de",
        "label": "Deutschland"
      },
      {
        "language_shortcode": "en",
        "label": "USA"
      }
    ]
  }
]

I want to only return German flags, where language_shortcode is de.

This is what I did:

const serialized = plainToInstance(CategoryResponseDto, entries, {
  excludeExtraneousValues: true,
});

This is my dto:

export class CategoryResponseDto {
  @Expose()
  @Transform(
    ({ value }) => {
      const de_lang = value.find((lang) => lang.language_shortcode === 'de');
      return de_lang.label;
    },
    { toPlainOnly: true },
  )
  languages: Language[];

  constructor(partial: Partial<CategoryResponseDto[]>) {
    Object.assign(this, partial);
  }
}

I get such a thing:

[
  {
    "languages": "ASP-Einrichtung"
  },
  {
    "languages": "Deutschland"
  }
]

I don't want to repeat useless languages objects. How can I achieve that?

Note: I can't use map to achieve what I want, because the result is array but the returned type of class transformer is CategoryResponseDto.

iradonov commented 3 months ago

The input type of plainToInstance is always a class constructor, so I'm afraid it cannot output a string array if you want to use decorators.

But why is your serialized not of array type? If entries is typed correctly to be an array, then serialized should be as well since plainToInstance is overloaded.

diffy0712 commented 2 months ago

Hello @vahidvdn,

the provided example does not work. You call plainToInstance, but the custom transformer has { toPlainOnly: true }, which will not run on plainToInstance. Please provide an expected output to make it clear what you are trying to achieve. I assume you want

['ASP-Einrichtung', 'Deutschland']

which is not possible as you want to use a class (dto), which is not possible in javascript.

You can achieve

{
    "languages": ["Deutschland", "ASP-Einrichtung"]
}

, but I do think, that class-transformer is not the best choice this case since you do not want a DTO, you only want an array. it is really simple without any lib:


const languages = entries.map(entry => entry.languages.find(language => language.language_shortcode === 'de')?.label).filter(Boolean)

Closing as answered. If you have any questions left feel free to comment on this issue.

vahidvdn commented 2 months ago

Thank you everyone. Yes, I decided not to proceed with class-transformer for this specific user case. I just wanted to make it as declarative as possible, but it's unsuitable for this case.

github-actions[bot] commented 1 month ago

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.