Class-Typescript mixin 上的验证器装饰器 类
Class-validator decorators on Typescript mixin classes
在我的项目中,我有一个具有许多属性的大对象。每个 属性 都有自己独特的验证,这些验证是使用 class validator decorators 对其执行的。 class 的每个 属性 都被描述为一个 mixin。然而,我注意到当将 mixin 应用到基础 class 时,只有最后传递的 mixin 有它的装饰器 运行 用于验证。
例如,我们有:
export class Property {
public async validate (): Promise<string[]> {
const result = await validate(this)
return result
}
}
export class Name extends Property {
@IsDefined()
@IsString()
@Length(5, 255)
name: string
}
export class Description extends Property {
@IsDefined()
@IsString()
@Length(16, 1000)
description: string
}
每个 属性 在单元测试时都正确地验证了自己。
在创建从混合宏继承的 class 时,我正在执行以下操作:
/**
* Applies the mixins to a class. Taken directly from the Typescript documentation.
*/
function applyMixins (derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
Object.defineProperty(derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor.prototype, name))
})
})
}
export class Foo {
public async validate (): Promise<any> {
const result = await validate(this)
return result
}
}
export interface Foo extends Name, Description { }
applyMixins(Foo, [Name, Description])
但是,当创建 Foo
的实例并在该实例上调用 .validate
时,我们只会收到 Description.
的错误
是否有一些不同的方法来应用混合以验证所有混合属性?
发生这种情况是因为 сlass-validator 使用原型来检测验证规则,我们需要将规则复制到派生 ctor 的原型。
我们可以这样做:
/**
* Applies the mixins to a class, but with class validator constraints.
*/
function applyMixinsWithValidators (derivedCtor: any, baseCtors: any[]) {
const metadata = getMetadataStorage() // from class-validator
// Base typescript mixin implementation
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
Object.defineProperty(derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor.prototype, name))
})
})
baseCtors.forEach(baseCtor => {
// Get validation constratints from the mixin
const constraints = metadata.getTargetValidationMetadatas(baseCtor.prototype.constructor, '')
for (const constraint of constraints) {
// For each constraint on the mixin
// Clone the constraint, replacing the target with the the derived constructor
let clone = {
...constraint,
target: derivedCtor.prototype.constructor
}
// Set the prototype of the clone to be a validation metadata object
clone = Object.setPrototypeOf(clone, Object.getPrototypeOf(constraint))
// Add the cloned constraint to class-validators metadata storage object
metadata.addValidationMetadata(clone)
}
})
}
在我的项目中,我有一个具有许多属性的大对象。每个 属性 都有自己独特的验证,这些验证是使用 class validator decorators 对其执行的。 class 的每个 属性 都被描述为一个 mixin。然而,我注意到当将 mixin 应用到基础 class 时,只有最后传递的 mixin 有它的装饰器 运行 用于验证。
例如,我们有:
export class Property {
public async validate (): Promise<string[]> {
const result = await validate(this)
return result
}
}
export class Name extends Property {
@IsDefined()
@IsString()
@Length(5, 255)
name: string
}
export class Description extends Property {
@IsDefined()
@IsString()
@Length(16, 1000)
description: string
}
每个 属性 在单元测试时都正确地验证了自己。
在创建从混合宏继承的 class 时,我正在执行以下操作:
/**
* Applies the mixins to a class. Taken directly from the Typescript documentation.
*/
function applyMixins (derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
Object.defineProperty(derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor.prototype, name))
})
})
}
export class Foo {
public async validate (): Promise<any> {
const result = await validate(this)
return result
}
}
export interface Foo extends Name, Description { }
applyMixins(Foo, [Name, Description])
但是,当创建 Foo
的实例并在该实例上调用 .validate
时,我们只会收到 Description.
是否有一些不同的方法来应用混合以验证所有混合属性?
发生这种情况是因为 сlass-validator 使用原型来检测验证规则,我们需要将规则复制到派生 ctor 的原型。
我们可以这样做:
/**
* Applies the mixins to a class, but with class validator constraints.
*/
function applyMixinsWithValidators (derivedCtor: any, baseCtors: any[]) {
const metadata = getMetadataStorage() // from class-validator
// Base typescript mixin implementation
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
Object.defineProperty(derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor.prototype, name))
})
})
baseCtors.forEach(baseCtor => {
// Get validation constratints from the mixin
const constraints = metadata.getTargetValidationMetadatas(baseCtor.prototype.constructor, '')
for (const constraint of constraints) {
// For each constraint on the mixin
// Clone the constraint, replacing the target with the the derived constructor
let clone = {
...constraint,
target: derivedCtor.prototype.constructor
}
// Set the prototype of the clone to be a validation metadata object
clone = Object.setPrototypeOf(clone, Object.getPrototypeOf(constraint))
// Add the cloned constraint to class-validators metadata storage object
metadata.addValidationMetadata(clone)
}
})
}