diff --git a/lib/intersection-type.helper.ts b/lib/intersection-type.helper.ts
index 6e385ccb..36260bd1 100644
--- a/lib/intersection-type.helper.ts
+++ b/lib/intersection-type.helper.ts
@@ -1,5 +1,6 @@
import { Type } from '@nestjs/common';
import {
+ inheritPropertyInitializers,
inheritTransformationMetadata,
inheritValidationMetadata,
} from './type-helpers.utils';
@@ -8,7 +9,12 @@ export function IntersectionType(
classARef: Type,
classBRef: Type,
): Type {
- abstract class IntersectionClassType {}
+ abstract class IntersectionClassType {
+ constructor() {
+ inheritPropertyInitializers(this, classARef);
+ inheritPropertyInitializers(this, classBRef);
+ }
+ }
inheritValidationMetadata(classARef, IntersectionClassType);
inheritValidationMetadata(classBRef, IntersectionClassType);
diff --git a/lib/omit-type.helper.ts b/lib/omit-type.helper.ts
index 1a2f5abc..63b29ac3 100644
--- a/lib/omit-type.helper.ts
+++ b/lib/omit-type.helper.ts
@@ -1,5 +1,6 @@
import { Type } from '@nestjs/common';
import {
+ inheritPropertyInitializers,
inheritTransformationMetadata,
inheritValidationMetadata,
} from './type-helpers.utils';
@@ -8,10 +9,15 @@ export function OmitType(
classRef: Type,
keys: readonly K[],
): Type> {
- abstract class OmitClassType {}
-
const isInheritedPredicate = (propertyKey: string) =>
!keys.includes(propertyKey as K);
+
+ abstract class OmitClassType {
+ constructor() {
+ inheritPropertyInitializers(this, classRef, isInheritedPredicate);
+ }
+ }
+
inheritValidationMetadata(classRef, OmitClassType, isInheritedPredicate);
inheritTransformationMetadata(classRef, OmitClassType, isInheritedPredicate);
diff --git a/lib/partial-type.helper.ts b/lib/partial-type.helper.ts
index 1864c182..1bff140f 100644
--- a/lib/partial-type.helper.ts
+++ b/lib/partial-type.helper.ts
@@ -1,18 +1,23 @@
import { Type } from '@nestjs/common';
import {
applyIsOptionalDecorator,
+ inheritPropertyInitializers,
inheritTransformationMetadata,
inheritValidationMetadata,
} from './type-helpers.utils';
export function PartialType(classRef: Type): Type> {
- abstract class PartialClassType {}
+ abstract class PartialClassType {
+ constructor() {
+ inheritPropertyInitializers(this, classRef);
+ }
+ }
const propertyKeys = inheritValidationMetadata(classRef, PartialClassType);
inheritTransformationMetadata(classRef, PartialClassType);
if (propertyKeys) {
- propertyKeys.forEach(key => {
+ propertyKeys.forEach((key) => {
applyIsOptionalDecorator(PartialClassType, key);
});
}
diff --git a/lib/pick-type.helper.ts b/lib/pick-type.helper.ts
index 56297357..10c2f0be 100644
--- a/lib/pick-type.helper.ts
+++ b/lib/pick-type.helper.ts
@@ -1,5 +1,6 @@
import { Type } from '@nestjs/common';
import {
+ inheritPropertyInitializers,
inheritTransformationMetadata,
inheritValidationMetadata,
} from './type-helpers.utils';
@@ -8,10 +9,14 @@ export function PickType(
classRef: Type,
keys: readonly K[],
): Type> {
- abstract class PickClassType {}
-
const isInheritedPredicate = (propertyKey: string) =>
keys.includes(propertyKey as K);
+
+ abstract class PickClassType {
+ constructor() {
+ inheritPropertyInitializers(this, classRef, isInheritedPredicate);
+ }
+ }
inheritValidationMetadata(classRef, PickClassType, isInheritedPredicate);
inheritTransformationMetadata(classRef, PickClassType, isInheritedPredicate);
diff --git a/lib/type-helpers.utils.ts b/lib/type-helpers.utils.ts
index 4cc46035..ed354b40 100644
--- a/lib/type-helpers.utils.ts
+++ b/lib/type-helpers.utils.ts
@@ -155,3 +155,26 @@ function isClassTransformerAvailable() {
return false;
}
}
+
+export function inheritPropertyInitializers(
+ target: Record,
+ sourceClass: Type,
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ isPropertyInherited = (key: string) => true,
+) {
+ try {
+ const tempInstance = new sourceClass();
+ const propertyNames = Object.getOwnPropertyNames(tempInstance);
+
+ propertyNames
+ .filter(
+ (propertyName) =>
+ typeof tempInstance[propertyName] !== 'undefined' &&
+ typeof target[propertyName] === 'undefined',
+ )
+ .filter((propertyName) => isPropertyInherited(propertyName))
+ .forEach((propertyName) => {
+ target[propertyName] = tempInstance[propertyName];
+ });
+ } catch {}
+}
diff --git a/tests/intersection-type.helper.spec.ts b/tests/intersection-type.helper.spec.ts
index 8467eea8..3d9d4c2a 100644
--- a/tests/intersection-type.helper.spec.ts
+++ b/tests/intersection-type.helper.spec.ts
@@ -6,18 +6,18 @@ import { getValidationMetadataByTarget } from './type-helpers.test-utils';
describe('IntersectionType', () => {
class ClassA {
@MinLength(10)
- login!: string;
+ login = 'defaultLoginWithMin10Chars';
- @Transform(str => str + '_transformed')
+ @Transform((str) => str + '_transformed')
@MinLength(10)
password!: string;
}
class ClassB {
@IsString()
- firstName!: string;
+ firstName = 'defaultFirst';
- @Transform(str => str + '_transformed')
+ @Transform((str) => str + '_transformed')
@MinLength(5)
lastName!: string;
}
@@ -27,7 +27,7 @@ describe('IntersectionType', () => {
describe('Validation metadata', () => {
it('should inherit metadata for all properties from class A and class B', () => {
const validationKeys = getValidationMetadataByTarget(UpdateUserDto).map(
- item => item.propertyName,
+ (item) => item.propertyName,
);
expect(validationKeys).toEqual([
'login',
@@ -43,17 +43,11 @@ describe('IntersectionType', () => {
const validationErrors = await validate(updateDto);
- expect(validationErrors.length).toEqual(4);
+ expect(validationErrors.length).toEqual(2);
expect(validationErrors[0].constraints).toEqual({
- minLength: 'login must be longer than or equal to 10 characters',
- });
- expect(validationErrors[1].constraints).toEqual({
minLength: 'password must be longer than or equal to 10 characters',
});
- expect(validationErrors[2].constraints).toEqual({
- isString: 'firstName must be a string',
- });
- expect(validationErrors[3].constraints).toEqual({
+ expect(validationErrors[1].constraints).toEqual({
minLength: 'lastName must be longer than or equal to 5 characters',
});
});
@@ -86,4 +80,12 @@ describe('IntersectionType', () => {
expect(transformedDto.password).toEqual(password + '_transformed');
});
});
+
+ describe('Property initializers', () => {
+ it('should inherit property initializers', () => {
+ const updateUserDto = new UpdateUserDto();
+ expect(updateUserDto.login).toEqual('defaultLoginWithMin10Chars');
+ expect(updateUserDto.firstName).toEqual('defaultFirst');
+ });
+ });
});
diff --git a/tests/omit-type.helper.spec.ts b/tests/omit-type.helper.spec.ts
index baf56ab7..cbde0005 100644
--- a/tests/omit-type.helper.spec.ts
+++ b/tests/omit-type.helper.spec.ts
@@ -10,7 +10,7 @@ describe('OmitType', () => {
@Transform((str) => str + '_transformed')
@MinLength(10)
- password!: string;
+ password = 'defaultPassword';
}
class UpdateUserDto extends OmitType(CreateUserDto, ['login']) {}
@@ -56,4 +56,12 @@ describe('OmitType', () => {
expect(transformedDto.password).toEqual(password + '_transformed');
});
});
+
+ describe('Property initializers', () => {
+ it('should inherit property initializers', () => {
+ const updateUserDto = new UpdateUserDto();
+ expect((updateUserDto as any)['login']).toBeUndefined();
+ expect(updateUserDto.password).toEqual('defaultPassword');
+ });
+ });
});
diff --git a/tests/partial-type.helper.spec.ts b/tests/partial-type.helper.spec.ts
index f050713d..64dd273e 100644
--- a/tests/partial-type.helper.spec.ts
+++ b/tests/partial-type.helper.spec.ts
@@ -12,7 +12,7 @@ describe('PartialType', () => {
}
class CreateUserDto extends BaseUserDto {
- login!: string;
+ login: string = 'defaultLogin';
@Expose()
@Transform((str) => str + '_transformed')
@@ -74,4 +74,11 @@ describe('PartialType', () => {
);
});
});
+
+ describe('Property initializers', () => {
+ it('should inherit property initializers', () => {
+ const updateUserDto = new UpdateUserDto();
+ expect(updateUserDto.login).toEqual('defaultLogin');
+ });
+ });
});
diff --git a/tests/pick-type.helper.spec.ts b/tests/pick-type.helper.spec.ts
index 5e7cd992..1c797a85 100644
--- a/tests/pick-type.helper.spec.ts
+++ b/tests/pick-type.helper.spec.ts
@@ -5,9 +5,9 @@ import { getValidationMetadataByTarget } from './type-helpers.test-utils';
describe('PickType', () => {
class CreateUserDto {
- @Transform(str => str + '_transformed')
+ @Transform((str) => str + '_transformed')
@MinLength(10)
- login!: string;
+ login = 'defaultLogin';
@MinLength(10)
password!: string;
@@ -18,7 +18,7 @@ describe('PickType', () => {
describe('Validation metadata', () => {
it('should inherit metadata with "password" property excluded', () => {
const validationKeys = getValidationMetadataByTarget(UpdateUserDto).map(
- item => item.propertyName,
+ (item) => item.propertyName,
);
expect(validationKeys).toEqual(['login']);
});
@@ -56,4 +56,12 @@ describe('PickType', () => {
expect(transformedDto.login).toEqual(login + '_transformed');
});
});
+
+ describe('Property initializers', () => {
+ it('should inherit property initializers', () => {
+ const updateUserDto = new UpdateUserDto();
+ expect((updateUserDto as any)['password']).toBeUndefined();
+ expect(updateUserDto.login).toEqual('defaultLogin');
+ });
+ });
});