I've been trying to use @Type() with itself:
import { Type } from 'class-transformer';
class User {
@Type(() => User, {
discriminator: {
property: 'type',
subTypes: [
{ value: HappyFriend, name: 'happy' },
{ value: SadFriend, name: 'sad' },
],
},
})
friend: HappyFriend | SadFriend;
}
class HappyFriend extends User {
doHappy() {}
}
class SadFriend extends User {
doSad() {}
}
but I can't because [ts] Class 'HappyFriend' used before its declaration. [2449] and [ts] Class 'SadFriend' used before its declaration. [2449]
Any idea what I can do here?
I managed to use @Transform() to do it. It calls a recursive function imported from another file which in turn imports the classes and uses a switch() to check. I was worried about a circular reference but apparently not.
Hello @patricknazar
I've encountered the same problem and still not able to find a proper solution. Can you please share the solution you came up with or at least the basics of the idea?
@hedegitsinue
import { Transform, TransformationType, classToPlain, plainToClass, classToClass } from 'class-transformer';
function transformFn(value: User, obj, type) {
if (type === TransformationType.PLAIN_TO_CLASS) {
return User.fromObject(value);
} else if (type === TransformationType.CLASS_TO_PLAIN) {
return classToPlain(value);
} else if (type === TransformationType.CLASS_TO_CLASS) {
return classToClass(value);
}
}
class User {
type: string = '';
name: string;
@Transform(transformFn)
friend: User;
sayHi() {
console.log(`${this.getMessage()}\n`);
}
getMessage() {
return `My name is "${this.name}", I am a *${this.friendlyType()}*${this.friend ? ` and my friend says:\n${this.friend.getMessage()}` : '.'}`;
}
friendlyType() { return 'Base User'; }
static fromObject(value) {
switch (value.type) {
case 'happy':
return plainToClass(HappyUser, value);
case 'sad':
return plainToClass(SadUser, value);
default:
return plainToClass(User, value);
}
}
}
class HappyUser extends User {
friendlyType() { return 'Happy User'; }
}
class SadUser extends User {
friendlyType() { return 'Sad User'; }
}
plainToClass(User, {
name: 'A',
friend: {
name: 'A\'s Friend',
type: 'happy',
}
}).sayHi();
plainToClass(User, {
name: 'B',
friend: {
name: 'B\'s Friend',
type: 'sad',
}
}).sayHi();
plainToClass(User, {
name: 'C',
friend: {
name: 'C\'s Friend',
type: 'unknown',
friend: {
name: '2nd Level Friend',
type: 'happy',
friend: {
name: '3rd Level Friend',
type: 'another',
}
}
}
}).sayHi();
Thank you very much! @patricknazar
No worries @hedegitsinue hope it helps!
@patricknazar
Today I found a way to use this functionality and maybe you will find this helpful.
What I did: I wanted to transform JSON to object that has children of same kind ("Tag" class). But it was giving me "Tag_1" is not defined. Then I thought that transform function was changing the metadata definitions. I decided to take the class name outside of transform function and export it with a constant. After that it started to work properly. And also @Type() decorator started to work.
@Exclude()
export class Tag {
@Expose({name: 'children_tags', toClassOnly: true})
/*@Transform((childrenTags) => {
if (childrenTags) {
return childrenTags.map(child => plainToClass(TagClass, child));
}
return childrenTags;
}, {toClassOnly: true})*/
@Type(() => TagClass)
children: Array<Tag> = [];
}
export const TagClass = Tag;
@hedegitsinue that's cool! However, apparently it is not working when using the discriminator option in @Type. I have multiple sub types and they all get resolved as undefined :/
If you still experience an issue with using discriminator with @Type decorator then please open a new issue and please
provide a __minimal__ reproducible example showcasing your problem. The example should:
Closing as outdated.
Most helpful comment
@hedegitsinue