Skip to content

Commit 818897b

Browse files
authored
fix: plainToClass throws an error when a union-type member is undefined (#521)
1 parent 90feca3 commit 818897b

File tree

2 files changed

+60
-4
lines changed

2 files changed

+60
-4
lines changed

src/TransformOperationExecutor.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { defaultMetadataStorage } from './storage';
2-
import { TypeHelpOptions, TypeOptions, ClassTransformOptions, TypeMetadata } from './interfaces';
2+
import { ClassTransformOptions, TypeHelpOptions, TypeMetadata, TypeOptions } from './interfaces';
33
import { TransformationType } from './enums';
44
import { getGlobal, isPromise } from './utils';
55

@@ -236,9 +236,11 @@ export class TransformOperationExecutor {
236236
type = subValue.constructor;
237237
}
238238
if (this.transformationType === TransformationType.CLASS_TO_PLAIN) {
239-
subValue[metadata.options.discriminator.property] = metadata.options.discriminator.subTypes.find(
240-
subType => subType.value === subValue.constructor
241-
).name;
239+
if (subValue) {
240+
subValue[metadata.options.discriminator.property] = metadata.options.discriminator.subTypes.find(
241+
subType => subType.value === subValue.constructor
242+
).name;
243+
}
242244
}
243245
} else {
244246
type = metadata;

test/functional/custom-transform.spec.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,60 @@ describe('custom transformation decorator', () => {
528528
}).not.toThrow();
529529
});
530530

531+
/**
532+
* test-case for issue #520
533+
*/
534+
it('should deserialize undefined union type to undefined', () => {
535+
defaultMetadataStorage.clear();
536+
expect(() => {
537+
abstract class Hobby {
538+
public name: string;
539+
}
540+
541+
class Sports extends Hobby {
542+
// Empty
543+
}
544+
545+
class Relaxing extends Hobby {
546+
// Empty
547+
}
548+
549+
class Programming extends Hobby {
550+
@Transform(({ value }) => value.toUpperCase())
551+
specialAbility: string;
552+
}
553+
554+
class Person {
555+
public name: string;
556+
557+
@Type(() => Hobby, {
558+
discriminator: {
559+
property: '__type',
560+
subTypes: [
561+
{ value: Sports, name: 'sports' },
562+
{ value: Relaxing, name: 'relax' },
563+
{ value: Programming, name: 'program' },
564+
],
565+
},
566+
})
567+
public hobby: Hobby;
568+
}
569+
570+
const model: Person = new Person();
571+
const sport = new Sports();
572+
sport.name = 'Football';
573+
const program = new Programming();
574+
program.name = 'typescript coding';
575+
program.specialAbility = 'testing';
576+
model.name = 'John Doe';
577+
// NOTE: hobby remains undefined
578+
model.hobby = undefined;
579+
const json: any = classToPlain(model);
580+
expect(json).not.toBeInstanceOf(Person);
581+
expect(json.hobby).toBeUndefined();
582+
}).not.toThrow();
583+
});
584+
531585
it('should transform class Person into class OtherPerson with different possibilities for type of one property (polymorphism)', () => {
532586
defaultMetadataStorage.clear();
533587
expect(() => {

0 commit comments

Comments
 (0)