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

Posted

技术标签:

【中文标题】如何在 TypeScript 类中正确声明计算属性名称?【英文标题】:How to properly declare computed property names in TypeScript classes? 【发布时间】:2020-10-07 02:49:16 【问题描述】:

总之

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

上下文

我有一个自定义元素类方法,它自动将任何具有data-reference 属性的子元素存储在一个属性中,该属性以它的值命名,前缀为'$'。 它在 vanilla 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&lt;any&gt;(加上括号)不是一个可行的解决方案。

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

【问题讨论】:

第二个选项是要走的路。 “任何以 '$' 开头的属性”的问题 - 无法真正验证 【参考方案1】:

typescript 无法对您的 html 代码生成静态检查,它只是不能让 html 成为动态的(您可以使用data-reference 属性动态生成 DOM)2 或 3 都是可行的选择。想一想:typescript 在编译时检查类型,在交付之前,HTML 可以在以后填充额外的元素,在运行时。

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

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

或者你可以只拥有一个普通的属性:

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

【讨论】:

好吧,我没想到打字稿会直接在 HTML 上检查,我想知道是否有类似于您的第一个示例的解决方案,例如 [x: '$' + string]: HTMLElement(当然这没有任何意义,但您明白了)。此外,这个想法是任何class SomeDerivingComponent extends SomeComponent 都会自动获得自己的参考,所以如果我必须明确它们,我有点错过了这一点。但我知道我正在寻找的东西不存在。感谢您的回答! 我不知道这个建议有多好(意思是请持保留态度),但在这种情况下我通常最终会做的是手动生成打字稿定义。就像编写一个小的 Python 脚本,它可能会解析您的 html(或其他东西,idk?)并生成 .ts 文件,其中包含您需要的类型定义。这实际上比听起来容易得多,但这是增加的支持。

以上是关于如何在 TypeScript 类中正确声明计算属性名称?的主要内容,如果未能解决你的问题,请参考以下文章

当计算使用后台线程时,如何正确声明计算属性?

如何通过数据库在 TypeScript 类中定义属性?

在类中声明第一个字段属性之前添加额外的行

如何在 TypeScript 中正确要求和声明节点库类型?

Typescript - 如何声明具有已知属性的匿名对象?

TypeScript基础入门之装饰器(三)