如何在 TypeScript 类 中正确声明计算的 属性 名称?

How to properly declare computed property names in TypeScript classes?

简而言之

如何在 TypeScript 中声明:在这个 class 中,任何以 '$' 开头的 属性 都是对元素 的引用?

上下文

我有一个自定义元素 class 方法,它自动将任何具有属性 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 种解决方法,但其中 none 完全令人满意。

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 我更接近了,但它仍然不等同于香草 JavaScript 版本。

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 还很陌生。 提前致谢!

typescript 无法对您的 html 代码生成静态检查,它不能给定 html 是动态的(您可以使用 [=12 动态生成 DOM =] attributes) 2 或 3 都是可行的选项。想一想:打字稿在编译期间检查类型,在交付之前,HTML 可以稍后在运行时填充额外的元素。

如果你事先知道你的元素,你可以为打字稿提供静态类型信息。例如对于 (3) 选项:

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

或者你可以只用普通的 属性:

class SomeComponent extends HTMLElement {
    $myProp: HTMLElement
    ....