Вы не можете хранить дополнительную информацию в собственности, как это.При назначении свойство будет заменено новым значением, и все, что вы назначаете, а не прототип класса (а target
- это prototype
не экземпляр класса), будет доступно после назначения нового значения.
Решением было бы добавить дополнительное свойство, назовем его meta
, которое может содержать эти метаданные (и другие метаданные свойства по мере необходимости).Вы даже можете ввести его правильно, используя сопоставленные типы.Это решение довольно близко к желаемому результату:
class Model {
readonly meta!: Metadata<Model>
@Required
name: string;
hobbies: string;
constructor(dto?: any) {
this.name = dto.name; // This clears my "required" property
this.hobbies = dto.hobbies;
}
}
type Metadata<T> = Partial<Record<keyof T, PropertyMetadata>>
type PropertyMetadata = {
required?: boolean
}
function Required<T extends { meta: Metadata<T> }, TKey extends keyof T>(target: T, key: TKey) {
let meta = target.meta || (target.meta = {});
let propMeta = meta[key] || (meta[key] = {})
propMeta!.required = true
}
console.log(new Model({}).meta.name!.required);
Обратите внимание, что meta
может быть неопределенным, если у вас нет метаданных.И метаданные для свойства также могут быть неопределенными, поэтому в угловом шаблоне вы должны получить к нему доступ, используя ?
: model.meta?.name?.required
.Вы могли бы также сделать несколько более причудливых вещей, назначив Proxy
вместо {}
на meta
, но я пошел простым путем.