Let’s start by recalling the example Component we’re working through:

Use the // @ts-ignore to simply ignore the type-unsafety of the lookup. This approach is preferable when you don’t know if any of the keys might be missing. If, for example, either some or nested were undefined or null , the lookup example above in (1) would fail.

TypeScript does not today and presumably never will be able to do that lookup. The workaround is to do one of two things:

As noted above, TypeScript cannot do a lookup for any place using nested keys—which means that this.get('some.nested.key') won’t type-check, sadly. This is an inherent limitation of the type system as it stands today, and for any future I can foresee. The problem is this: what exactly is 'some.nested.key' ? It could be what we use it for in the usual scenario in Ember, of course: a string representing a lookup on a property of a property of a property of whatever this is. But it could equally well be a key named 'some.nested.key' . This is perfectly valid JavaScript, after all:

In the case of isLoggedIn , the bool helper only ever returns a boolean, so the type of isLoggedIn is ComputedProperty<boolean> . In the case of savedUser , since TypeScript can’t figure out what the nested key means, we have to specify it explicitly, using Computed<Person> . In these cases, you have to do the work yourself to check that the type you specify is the correct type. If you write down the wrong type here, TypeScript will believe you (it doesn’t have any other good option!) and you’ll be back to things blowing up unexpectedly at runtime.

Beyond computed , there are a lot of other computed property tools we use all the time. Some of them can (and therefore do) infer the type of the resulting computed property correctly. But there are a bunch of idiomatic things that TypeScript does not and cannot validate – a number of the computed property macros are in this bucket, because they tend to be used for nested keys, and as noted above, TypeScript does not and cannot validate nested keys like that.

The other really important thing to note here is the use of this: MyComputed . By doing this, we’re telling TypeScript explicitly that the type of this in this particular function is the class context. We have to do this here, because we don’t have any way to tell the computed helper itself that the function inside it will be bound to the this context of the containing class. Put another way: we don’t have any other way to tell TypeScript that one of the things computed does is bind this appropriately to the function passed into it; but gladly we do have this way—otherwise we’d be out of luck entirely! (You’ll see the same thing below when we look at actions). The boilerplate is a bit annoying, admittedly—but it at least makes it type-check.

Whichever way you do it, TypeScript will correctly infer the type of the computed property in question (here fromModel ) as long as you explicitly annotate the return type of the callback passed to computed . Accordingly, in this case, the type of fromModel is ComputedProperty<string> . The fact that it’s a ComputedProperty means if you try to treat it as a plain string, without using Ember.get to unwrap it, TypeScript will complain at you.

When using a computed property in the brave new world of ES6 classes, we normally just assign them as instance properties. As mentioned in the previous post , and in line with my comments above, this has some important tradeoffs around performance. If you need the absolute best performance, you can continue to install them on the prototype by doing this instead:

We already covered component arguments and injections as well as basic class properties and the exceptions to normal class-property ways of doing things, in Parts 1 and 2 . With that background out of the way, we can now turn to computed properties. I’m including the component arguments in this code sample because they’re referenced in the computed property. Assume Person is a pretty “person” representation, with a firstName and a lastName and maybe a few other properties.

As with computed properties, we need the this type declaration to tell TypeScript that this method is going to be automatically bound to the class instance. Otherwise, TypeScript thinks the this here is the actions hash, rather than the MyComponent class.

By and large, you can get away with using the same this: MyComponent trick when hacking around prototypal extension problems, or performance problems, by putting computed properties in a .extend({...} block. However, you will sometimes see a type error indicating that the class is referenced in its own definition expression. In that case, you may need to judiciously apply any , if you can’t make it work by using normal class properties.

constructor and class methods

ES6 class constructors and class methods both work as you’d expect, though as we’ll see you’ll need an extra bit of boilerplate for methods, at least for now.

constructor() { super(); assert('`model` is required', !isNone(this.model)); this.includeAhoy(); } includeAhoy(this: AnExample): void { if (!this.get('aCollection').includes('ahoy')) { this.set('aCollection', current.concat('ahoy')); } }

For the most part, you can just switch to using normal ES6 class constructors instead of the Ember init method. You can, if you so desire, also move existing init functions passed to a .extends({ ...}) hash to class methods, and they’ll work once you change this._super(...arguments) to super.init(...arguments) . It’s worth pausing to understand the relationship between init and prototypal init and the constructor . An init in the .extends() hash runs first, then an init method on the class, then the normal constructor .

Note that you do not need to (and cannot) annotate the constructor with this: MyComponent . Depending on the class you’re building, you may occasionally have type-checking problems that come up as a result of this. I’ve only ever seen that happen when using computed properties while defining a proxy, but it does come up. In that case, you can fall back to using init as a method, and set this: MyComponent on it, and things will generally fall out as working correctly at that point. When it comes up, this seems to be just a limitation of what this is understood to be in a constructor given Ember’s rather more-complex-than-normal-classes view of what a given item being constructed is.

Other class methods do also need the this type specified if they touch computed properties. (Normal property access is fine without it.) That’s because the lookups for ComputedProperty instances (using Ember.get or Ember.set ) need to know what this is where they should do the lookup, and the full this context isn’t inferred correctly at present. You can either write that on every invocation of get and set , like (this as MyComponent).get(...) , or you can do it once at the start of the method. Again, a bit boiler-platey, but it gets the job done and once you’re used to it it’s minimal hassle.