Open
Description
Currently I'm trying to extends a Require Schema class but want to make it reuse by PartialType to prevent redeclare the field.
Anyway to achieve this?
Framework: Express JS + Tsoa
I have tried this but it seem does not work.
// Utility function to create PartialType (similar to NestJS)
export function PartialType<T>(BaseClass: new () => T): new () => Partial<T> {
class PartialClass {}
// Copy metadata from the base class
const baseClassPrototype = BaseClass.prototype;
const partialClassPrototype = PartialClass.prototype;
// Get all property names from the base class
const propertyNames = Object.getOwnPropertyNames(baseClassPrototype);
// Copy validation metadata for each property
propertyNames.forEach(propertyName => {
if (propertyName === 'constructor') return;
// Get existing validation metadata
const validationMetadata = Reflect.getMetadata('custom:validation', baseClassPrototype, propertyName) || [];
const typeMetadata = Reflect.getMetadata('design:type', baseClassPrototype, propertyName);
// Apply IsOptional decorator to make the property optional
IsOptional()(partialClassPrototype, propertyName);
// Preserve existing validation decorators
if (typeMetadata) {
Reflect.defineMetadata('design:type', typeMetadata, partialClassPrototype, propertyName);
}
// Copy validation metadata
const existingValidators = Reflect.getMetadataKeys(baseClassPrototype, propertyName);
existingValidators.forEach(key => {
const metadata = Reflect.getMetadata(key, baseClassPrototype, propertyName);
if (metadata && key !== 'custom:validation') {
Reflect.defineMetadata(key, metadata, partialClassPrototype, propertyName);
}
});
});
return PartialClass as new () => Partial<T>;
}
// Alternative simpler implementation using class-validator's internal metadata
export function PartialTypeSimple<T>(BaseClass: new () => T): new () => Partial<T> {
class PartialClass extends BaseClass {}
// Get all properties from the base class
const instance = new BaseClass();
const propertyNames = Object.getOwnPropertyNames(instance);
// Apply IsOptional to each property
propertyNames.forEach(propertyName => {
IsOptional()(PartialClass.prototype, propertyName);
});
return PartialClass as new () => Partial<T>;
}
// Even simpler approach using mixin pattern
export function PartialTypeMixin<T>(BaseClass: new () => T) {
return class extends BaseClass {
constructor() {
super();
// Apply IsOptional to all properties
const propertyNames = Object.getOwnPropertyNames(this);
propertyNames.forEach(propertyName => {
IsOptional()(this.constructor.prototype, propertyName);
});
}
} as new () => Partial<T>;
}
// Most practical approach - manual but reliable
export function createPartialType<T>(BaseClass: new () => T): new () => Partial<T> {
// Create a new class that extends the base class
class PartialClass {}
// Copy the prototype
Object.setPrototypeOf(PartialClass.prototype, BaseClass.prototype);
// Get all property descriptors
const baseInstance = new BaseClass();
const propertyNames = Object.getOwnPropertyNames(baseInstance);
// Make all properties optional
propertyNames.forEach(propertyName => {
if (propertyName !== 'constructor') {
IsOptional()(PartialClass.prototype, propertyName);
}
});
return PartialClass as new () => Partial<T>;
}