Как правильно объявить имена вычисляемых свойств в классах TypeScript? - PullRequest
0 голосов
/ 17 июня 2020

Вкратце

Как я могу объявить в TypeScript: В этом классе любое свойство, начинающееся с '$', является ссылкой на элемент ?

Контекст

У меня есть метод класса Custom Element, который автоматически сохраняет любой дочерний элемент с атрибутом data-reference в свойстве, названном по его значению с префиксом «$». Прекрасно работает в ванили JavaScript:

class SomeComponent extends HTMLElement {
  connectedCallback() {
    this.setReferences()
    console.log(this.$myRef) // <div data-reference="myRef"></div>
  }

  setReferences() {
    const referencedElements = this.querySelectorAll('[data-reference]')

    referencedElements.forEach((element) => {
      const referenceName = '$' + element.getAttribute('data-reference')
      this[referenceName] = element
    })
  }
}
customElements.define('some-component', SomeComponent)
<some-component>
  <div data-reference="myRef"></div>
</some-component>

Но это не будет компилироваться в TypeScript, кто не знает о свойстве $myRef:

console.log(this.$myRef) // Property '$myRef' does not exist on type 'SomeComponent'
                 ^^^^^^

Пока что я Я придумал 3 обходных пути, но ни один из них не удовлетворяет полностью.

1. Утверждение типа

Нет!

console.log((this as any).$myRef) // <div data-reference="myRef"></div>

Смысл в том, чтобы обеспечить быстрый доступ к любому $ref, поэтому добавление as any или <any> (плюс круглые скобки) нецелесообразно раствор.

2. Литеральный объект

Вероятно, самый чистый, но не совсем то, чего я хочу достичь.

type ElementReferenceMap = { [key: string]: Element }

class SomeComponent extends HTMLElement {
    ref: ElementReferenceMap = {};

    setReferences() {
        const referencedElements = this.querySelectorAll('[data-reference]');

        referencedElements.forEach((element) => {
            const referenceName = element.getAttribute('data-reference');
            this.ref[referenceName] = element as Element;
        })

        console.log(this.ref.myRef); // <div data-reference="myRef"></div>
    }
}

Переименовав его в this.$.myRef Я подхожу ближе, но он все еще не эквивалент ванили * Версия 1048 *.

3. Свойство индекса

Работает, как ожидалось (как версия JavaScript), но кажется неправильным, поскольку допускает любое значение свойства.

class SomeComponent extends HTMLElement {
    // [x: string]: Element; // conflict with other properties
    [x: string]: any; // ok

    setReferences() {
        const referencedElements = this.querySelectorAll('[data-reference]');

        referencedElements.forEach((element) => {
            const referenceName = '$' + element.getAttribute('data-reference');
            this[referenceName] = element;
        })

        console.log(this.$myRef); // <div data-reference="myRef"></div>
    }
}

Я могу упустить очевидное решение, так как я новичок в TypeScript. Заранее спасибо!

1 Ответ

1 голос
/ 17 июня 2020

Машинописный текст не может генерировать проверки c для вашего html кода, он просто не может дать html быть динамическим c (вы можете динамически генерировать DOM с атрибутами data-reference) 2 или 3 оба варианта являются жизнеспособными. Подумайте об этом: машинописный текст проверяет типы во время компиляции, перед доставкой, когда HTML может быть заполнено дополнительными элементами позже, во время выполнения.

Если вы знаете свои элементы заранее, вы можете предоставить stati c введите информацию для машинописного текста. Например, для варианта (3):

class SomeComponent extends HTMLElement {
    [x: '$myRef' | '$myOtherRef']: HTMLElement; // ok
    ....

Или у вас может быть простое свойство:

class SomeComponent extends HTMLElement {
    $myProp: HTMLElement
    ....
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...