Декораторы свойств TypeScript - PullRequest
0 голосов
/ 07 марта 2019

Я хочу изолировать валидацию модели внутри самого класса модели, без необходимости жестко кодировать требуемые состояния (наши разработчики не знают, что требуется, просто им нужно подключить свойства). Я безуспешно пытаюсь найти путь к декоратору свойства. Конструктор перезаписывает то, что устанавливает декоратор. Это вообще возможно, я должен пойти по-другому?

model.ts

@Required
name: string;

hobbies: string;

constructor(dto?: any) {
    this.name = dto.name; // This clears my "required" property
    this.hobbies = dto.hobbies;
}

decorator.ts

function Required(target: any, key: string) {
    Object.defineProperty(target, "required", { value: true });
}

Чего я надеюсь достичь ...

*. HTML

<input name="name" [(ngModel)]="model.name" [required]="model.name.hasOwnProperty('required')">

Я также пытался поиграть с самим прототипом String, чтобы инициализировать это свойство, но конструктор по-прежнему очищает все, что установлено.

String.prototype["required"] = false;

1 Ответ

1 голос
/ 07 марта 2019

Вы не можете хранить дополнительную информацию в собственности, как это.При назначении свойство будет заменено новым значением, и все, что вы назначаете, а не прототип класса (а 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, но я пошел простым путем.

...