class-transformer icon indicating copy to clipboard operation
class-transformer copied to clipboard

question: Use `strategy: 'excludeAll'` with some explicitly untyped data

Open Cyberuben opened this issue 1 year ago • 2 comments

I was trying to... Almost all of my code is typed properly, with DTOs and validation on input data. All my responses are either mapped to entities (from TypeORM), or DTOs which I explicitly convert to these DTOs before returning them as plain objects again, to prevent fields that I don't want public from leaking out.

Now I'm dealing with some data which I'm not able to build the typing for, as it is calling an external API and passing through the response. I want to return this data anyway.

The problem: https://stackblitz.com/edit/typescript-playground-ec9vct?file=index.ts

In the code above, you can see that the object in both stages is just an empty object. How can I have class transformer accept that this is untyped data?

Cyberuben avatar Jul 15 '24 21:07 Cyberuben

Hello @Cyberuben,

it seems that when excludeAll strategy is used, the value does not contain the nested object for some reason. This might be a bug, I have to look at this a bit if I have time.

But to answer your question. You can use the following transform @Transform(({key, obj}) => obj[key]).

Hope this helps :)

diffy0712 avatar Jul 25 '24 08:07 diffy0712

@Cyberuben This worked for me:

class MyClass {
  @Expose()
  @Transform(transfromExposeAll())
  foo: any;

  public constructor(foo: any) {
    this.foo = foo;
  }
}
/** Exposes all nested objects when strategy is set to `excludeAll` */
export const transfromExposeAll = () => {
  @Expose()
  class ExposeAll {}

  @Expose()
  class ExposeAllMap extends Map<unknown, unknown> {}

  @Expose()
  class ExposeAllSet extends Set<unknown> {}

  const transform = (source: unknown): unknown => {
    if (Array.isArray(source)) {
      return source.map(transform);
    } else if (source instanceof Set) {
      return new ExposeAllSet(Array.from(source).map(transform));
    } else if (source instanceof Map) {
      return new ExposeAllMap(Array.from(source.entries()).map(([key, value]) => [key, transform(value)]));
    } else if (typeof source === "object" && source !== null) {
      return Object.assign(
        new ExposeAll(),
        Object.fromEntries(Object.entries(source).map(([key, value]) => [key, transform(value)])),
      );
    } else {
      return source;
    }
  };

  return ({ key, obj }: Pick<TransformFnParams, "key" | "obj">) => transform((obj as Record<string, unknown>)[key]);
};

aravindanve avatar Dec 23 '24 11:12 aravindanve