Class-transformer: question: `@Expose` decorator at a class level creates objects with unwanted empty properties

Created on 17 May 2018  路  10Comments  路  Source: typestack/class-transformer

When @Expose() is applied at the class level and plainToClass is called with the strategy excludeAll, empty properties from the plain object are left over.

e.g

@Expose()
export class EntityClassExpose {
  id: string;
}

export class EntityPropertyExpose {
  @Expose() id: string;
}

const blob = {
  id: "fred",
  junk: {
    junk: "junk"
  }
};

const x = plainToClass(EntityClassExpose, blob, { strategy: "excludeAll" });
const y = plainToClass(EntityPropertyExpose, blob, { strategy: "excludeAll" });

console.log(x);
console.log(y);

Will print out something like:

EntityClassExpose { id: 'fred', junk: {} }
EntityPropertyExpose { id: 'fred' }

A working example can be found here: https://github.com/shusson/expose-example.

Is this intended? Is there a way to ensure only the properties in a class are transformed without decorating every property?

needs docs question

Most helpful comment

I have also observed this issue. Using @Expose at the class level seems to do nothing. Is it expected to do something?

All 10 comments

@shusson Thanks for you investigation.

I'm not sure about this.

@bojack Do you know this? Maybe we should add a statement to the documentation.

Hi @shusson!

class-transformer won't remove any unknown property without asking it to do so, you can go two directions whitelisting, or blacklisting:

@Exclude() // everything is removed from the object, except the exposed properties
class WhiteListed {
  id: number; // won't exist in the transformed class

  @Expose()
  name: string;
}

// Everything is included except blacklisted properties
class WhiteListed {
  id: number; // it will exist in the transformed class

  @Exclude()
  name: string;
}

This is by decision, if you need this for data validation, you can use the whitelisting option in class-validator:

import {validate} from "class-validator";
// ...
validate(post, { whitelist: true });

This way, class-validator will remove every unknown property.

I will go ahead and close this, if you have any question regarding the topic, please feel free to continue the discussion.

@NoNameProvided I'm using the exclude strategy e.g

const x = plainToClass(EntityClassExpose, blob, { strategy: "excludeAll" });
const y = plainToClass(EntityPropertyExpose, blob, { strategy: "excludeAll" });

I would expect both x and y to be the same because the only difference is where I placed the @Expose decorator. One is at the class level, the other is at a property level.

@shusson You are right. When fields are added that are not given on that class, but are added, it is npt that class anymore.

I would also assume that only the wanted fields (defined on the class are given)

Aside: Shoudln't this be always the case?

@NoNameProvided or @MTschannett can we re-open this ticket please?

Sure. If you want to help to fix this. Feel free to open a PR.

For now a workaround is the method mentioned by @NoNameProvided.

But i think this is not a ideal solution

@NoNameProvided , Willing to work in this bug. Need help here, what would be perfect approach.

I have also observed this issue. Using @Expose at the class level seems to do nothing. Is it expected to do something?

Is this intended? Is there a way to ensure only the properties in a class are transformed without decorating every property?

This is intended or more likely a limitation of how JS works. The library cannot know what properties a class has without being decorated with a decorator. Eg:

class MyExampleClass {
  public someProperty!: string;
}

const instance = new MyExampleClass();
// inteliSense sees instance.someProperty and you can set it but
console.log(Object.keys(instance));
// returns [] when the key is not set to a concrete value 
// if we set some value to it we will see the key, but not before

For your example, it actually for the EntityClassExpose it should return an empty object, so it's incorrect still.

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.

Was this page helpful?
0 / 5 - 0 ratings