Utilizing the property decorator Field
, which adds its key to a fields
Reflect metadata property:
export function Field(): PropertyDecorator {
return (target, key) => {
const fields = Reflect.getMetadata('fields', target) || [];
if (!fields.includes(key)) {
fields.push(key)
}
Reflect.defineMetadata('fields', fields, target)
}
}
An abstract base-class Form
then accesses this metadata through a getter method:
abstract class Form {
get fields() {
return Reflect.getMetadata('fields', this) || [];
}
}
This setup allows for distinguishing form fields from other class properties. Example usage with these classes:
abstract class UserForm extends Form {
@Field()
public firstName: string
@Field()
public lastName: string
get fullName() {
return this.firstName + ' ' + this.lastName;
}
}
class AdminForm extends UserForm {
@Field()
roles: string[]
}
const form = new AdminForm()
console.log(form.fields)
// ['roles', 'firstName', 'lastName']
However, an issue arises when creating a sister class to AdminForm
- MemberForm
. When multiple subclasses exist for Form
, the fields
getter returns all fields:
class MemberForm extends UserForm {
@Field()
memberSince: Date;
}
const form = new AdminForm()
console.log(form.fields)
// ['roles', 'firstName', 'lastName', 'memberSince'] <--!!!
This behavior seems unexpected. Why does the memberSince
field show up on an instance of AdminForm
? How can different fields be defined for different subclasses?