الزخارف في تايبسكريبت + بيانات التعريف Reflect
أنا أستخدم مزين خاصية Field الذي يدفع مفتاحه إلى خاصية بيانات التعريف fields في Reflect:
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)
}
}
بعد ذلك لدي فئة أساسية مجردة Form تصل إلى البيانات الوصفية في ملحق الحصول:
abstract class Form {
get fields() {
return Reflect.getMetadata('fields', this) || [];
}
}
لقد تمكنت حتى الآن من استخدامه بنجاح لتمييز حقول النموذج عن الخصائص الأخرى للفئة. انظر إلى هذه الفئات:
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']
تظهر المشكلة عندما أقوم بتعريف فئة أخت لـ AdminForm - MemberForm. عندما توجد عدة فئات فرعية لـ Form، يبدو أن الخاصية fields تعيد جميع الحقول:
class MemberForm extends UserForm {
@Field()
memberSince: Date;
}
const form = new AdminForm()
console.log(form.fields)
// ['roles', 'firstName', 'lastName', 'memberSince'] <--!!!
هذا لا معنى له بالنسبة لي. لماذا يظهر حقل memberSince على نسخة من AdminForm؟ كيف يمكنني تعريف حقول مختلفة في الفئات الفرعية المختلفة؟
الإجابة رقم 1
المشكلة هي أن getMetadata ينزل في سلسلة النموذج الأولي وسيعيد دائمًا ما تم تعريفه على النوع الأساسي (نظرًا لأنه يتم تعيينه أولاً). تحتاج إلى استخدام getOwnMetadata للحصول على حقل المصفوفة للفئة الحالية فقط عند إضافة حقل جديد، وعند الحصول على الحقول تحتاج إلى صعود سلسلة الخصائص للحصول على جميع حقول الفئة الأساسية.
يجب أن يعمل هذا:
import 'reflect-metadata'
export function Field(): PropertyDecorator {
return (target, key) => {
const fields = Reflect.getOwnMetadata('fields', target) || [];
if (!fields.includes(key)) {
fields.push(key)
}
Reflect.defineMetadata('fields', fields, target)
}
}
abstract class Form {
get fields() {
let fields = []
let target = Object.getPrototypeOf(this);
while(target != Object.prototype) {
let childFields = Reflect.getOwnMetadata('fields', target) || [];
fields.push(...childFields);
target = Object.getPrototypeOf(target);
}
return fields;
}
}
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 form1 = new AdminForm()
console.log(form1.fields) // ['roles', 'firstName', 'lastName']
class MemberForm extends UserForm {
@Field()
memberSince!: Date;
}
const form2 = new MemberForm()
console.log(form2.fields) // ["memberSince", "firstName", "lastName"]
