使用TypeScript和Ember编写计算属性的正确方法是什么?(What is the proper way to write a computed property using TypeScript and Ember?)

我有一个相对较小的Ember / TypeScript应用程序,我已经工作了大约六个月。 我习惯于按如下定义计算属性:

@computed('styleNamespace', 'status') statusClass(componentClassName: string, status: string): string { return `${componentClassName}--${status}` }

但我从来没有能够通过TypeScript检查来正确地进行检查。 在回顾Chris Krycho的Emberconf培训资料后 ,似乎“正确”的做法如下:

@computed('styleNamespace', 'status') get statusClass(this: CertificateCard): string { return `${this.get('styleNamespace')}--${this.get('status')}` }

它是否正确? 我似乎错过了一些东西,但因为我仍然得到这样的错误:

Error: Assertion Failed: Attempted to apply @computed to statusClass, but it is not a native accessor function. Try converting it to `get statusClass()`

I have a relatively small Ember/TypeScript app that I have been working on for about six months. I used to define computed properties as follows:

@computed('styleNamespace', 'status') statusClass(componentClassName: string, status: string): string { return `${componentClassName}--${status}` }

But I have never been able to get this to pass TypeScript checking properly. After reviewing Chris Krycho's Emberconf training materials, it seems that the "proper" way to do this is as follows:

@computed('styleNamespace', 'status') get statusClass(this: CertificateCard): string { return `${this.get('styleNamespace')}--${this.get('status')}` }

Is this correct? I seem to be missing something, though because I am still getting errors like this:

Error: Assertion Failed: Attempted to apply @computed to statusClass, but it is not a native accessor function. Try converting it to `get statusClass()`

最满意答案

为了使装饰器在Ember.js中使用TypeScript,您至少需要使用ember-decorators 2.0.0(在此答案时可用于2.0.0-beta.2 )并设置"experimentalDecorators": true你的tsconfig.json的"compilerOptions"部分。

然后,对于3.1之前的所有Ember版本,你将像这样编写计算机(如在你的第二个例子中,但是对于后来偶然发现的其他人来说更完整地写出来)。 请注意,我们不需要getter的返回类型,因为它可以通过TypeScript正确推断(不像经典的Ember计算属性回调,其中返回类型需要显式)。

import Component from '@ember/component'; import { computed } from '@ember-decorators/object'; export default class TheComponent extends Component { styleNamespace: string; status: string; @computed('styleNamespace', 'status') get statusClass(this: CertificateCard) { return `${this.get('styleNamespace')}--${this.get('status')}` } }

从稳定Ember RFC#281的 Ember 3.1开始,您可以通过将this.get放到任何不涉及代理的属性来进一步简化此操作。 请注意,您也可以放弃this类型的声明。

import Component from '@ember/component'; import { computed } from '@ember-decorators/object'; export default class TheComponent extends Component { styleNamespace: string; status: string; @computed('styleNamespace', 'status') get statusClass() { return `${this.styleNamespace}--${this.status}`; } }

(另外:如果属性已知不是计算属性,而是例如在构建时传入组件的简单字符串,则甚至可以在Ember 3.1之前执行此操作。)


关于对装饰器提议的未来和稳定性的任何可能的担忧:对规范的任何建议的更改都不会对我们在Ember.js中使用的装饰器的消费者产生影响。 他们将需要实现者(例如,在ember-decorators项目上工作的团队)进行更改,但是消费代码(即在常规应用程序和插件中)将不受影响。

To make decorators work with TypeScript in Ember.js, you'll need to use at least ember-decorators 2.0.0 (available at the time of this answer as 2.0.0-beta.2) and set "experimentalDecorators": true in the "compilerOptions" section of your tsconfig.json.

Then, for all versions of Ember prior to 3.1 you'll write computeds like so (as in your second example, but written out more fully for anyone else who stumbles on this later). Note that we don't need the return type for the getter, as it can be properly inferred by TypeScript (unlike classic Ember computed property callbacks, where the return type was required to be explicit).

import Component from '@ember/component'; import { computed } from '@ember-decorators/object'; export default class TheComponent extends Component { styleNamespace: string; status: string; @computed('styleNamespace', 'status') get statusClass(this: CertificateCard) { return `${this.get('styleNamespace')}--${this.get('status')}` } }

Starting with Ember 3.1, which stabilizes Ember RFC #281, you'll be able to further simplify this by dropping this.get for any property which does not involve Proxies. Note that you'll also be able to drop the this type declarations then.

import Component from '@ember/component'; import { computed } from '@ember-decorators/object'; export default class TheComponent extends Component { styleNamespace: string; status: string; @computed('styleNamespace', 'status') get statusClass() { return `${this.styleNamespace}--${this.status}`; } }

(As an aside: if the properties are known not to be computed properties, but are e.g. simple strings passed into the component at construction time, you can do this even prior to Ember 3.1.)


Regarding any possible concerns about the future and stability of the decorators proposal: no proposed changes to the spec will have an impact on the consumers of the decorators we're using in Ember.js. They will require implementors (e.g. the team working on the ember-decorators project) to make changes, but consuming code (i.e. in regular apps and addons) will be unaffected.

更多推荐