GeistHaus
log in · sign up

https://jamescdavis.com/rss

rss
16 posts
Polling state
Status active
Last polled May 19, 2026 10:44 UTC
Next poll May 20, 2026 13:31 UTC
Poll interval 86400s
ETag W/"7c6c-sCGGMcaFMg2wIPEHT1S50adOGjo"

Posts

Declare your injections, or risk losing them!
emberTypeScript

When using Ember + TypeScript, the recommendation thus far for service injections has been to mark them as definite with ! (since they are not initialized in the class, but by the service injection decorator), e.g.

import { inject as service } from '@ember/service';

export default class MyComponent extends Component
Show full content

When using Ember + TypeScript, the recommendation thus far for service injections has been to mark them as definite with ! (since they are not initialized in the class, but by the service injection decorator), e.g.

import { inject as service } from '@ember/service';

export default class MyComponent extends Component {
  @service myService!: MyService;
}

This has worked fine so far, but a compiler flag introduced in TypeScript 3.7 breaks this. The flag is useDefineForClassFields, which was added to better align with how public class fields will most likely be standardized. This flag is not yet enabled by default, but could be at some point in the future. You can read all about it in the TypeScript 3.7 release notes.

Note: The corresponding flag in @babel/plugin-proposal-class-properties is enabled by default (well, disabled since in Babel the loose option turns off using Object.defineProperty for class fields) but the Babel implementation of decorators checks if a decorator has not added an initializer and if not, clears the descriptor then later skips Object.defineProperty if the descriptor has been cleared. This means that (at least for now) service injection using ! will continue to work for users of ember-cli-typescript, but there's no guarantee that this will always be the case. The safer bet is to use declare (and it's describing the desired behavior better as I explain below).

The change in how class fields are initialized (in particular the fact that they'll be initialized to undefined if they have no instance initializer) breaks using a decorator for service injection. To demonstrate, consider the following decorator and class:

function service(target: any, propertyName: string) {
  target[propertyName] = "I'm a service!";
}

class Foo {
  @service myService!: unknown;
}

const foo = new Foo();

console.log(`What are you? ${foo.myService}`);

What we hope will happen is that foo.myService will be I'm a service!. And, in fact, it is by default:

What are you? I'm a service!

Try it yourself with this TypeScript Playground link
(click Run and see the Logs tab on the right)

But watch what happens when we enable useDefineForClassFields:

What are you? undefined

TypeScript Playground link

Not exactly what we were hoping for! ?

If you look closely at the transpiled output (in the .JS tab) at the TypeScript Playground link above, you can see why this happened.

tl;dr? Object.defineProperty in the constructor overwrote the service injection on the class prototype, setting the value to undefined (void 0 evaluates to undefined).

The good news is TypeScript has a way to fix this.

So how do we fix it? We declare it!

The declare property modifier

If you've been using TypeScript for a bit, you've likely seen the declare keyword used to define ambient types, e.g.

declare function notWrittenInTypeScript(param1: string): boolean;

Which means: "ok TypeScript, I know you can't see that there's a function called notWrittenInTypescript that takes a string an returns a boolean, but trust me, it'll be there at run-time". These are super useful for defining types for things the TypeScript compiler doesn't know about like libraries written in JavaScript.

Along with useDefineForClassFields, TypeScript 3.7 introduced the ability to use declare on a class property, e.g.

class Foo {
  @service declare myService: unknown;
}

Using declare this way means basically the same thing as other uses. You're saying to the TypeScript compiler, "something outside of TypeScript will create and initialize this property". Which is exactly what service injection is doing! You don't do any initialization yourself in the class. Ember magic does it for you! As an added benefit, you no longer need to mark the property as definite with ! because no initializer is expected (in fact, it's an error if you try to add ! or an initializer).

Because declare tells TypeScript to just use the property for type information and don't try to initialize it, it fixes the issue with useDefineForClassFields:

What are you? I'm a service!

TypeScript Playground link

This syntax is also more exact than using ! because it's saying "something external to this class will initialize this property" rather than just "this property will be initialized". And declare will prevent someone from coming along and adding an initializer.

class Foo {
  @service declare myService: unknown = 'something'; // Error
}

For extra safety, you could even denote it as readonly:

class Foo {
  @service declare readonly myService: unknown;
  
  bar() {
    this.myService = 'something'; // Error
  }
}
Summary

So there you have it! In order to future-proof your service injections and not risk losing them, you should migrate to using the declare property modifier. To use declare in Ember, you'll need at least TypeScript 3.7, Babel 7.11, and ember-cli-typescript 4.0 (4.0.0-rc.1 is available as of this writing).

5f2391109c942100011a5299
Extensions
Using ember-concurrency with TypeScript
ember-concurrencyTypeScriptemberember.jsemberjsember octane

ember-concurrency is an amazing tool for managing asynchronous tasks in Ember applications. Until recently, the TypeScript story for ember-concurrency has not been great with no official types, problems with type-safety, and reliance on a proof-of-concept only available in an alpha release. Recent work by Godfrey Chan and the author, with

Show full content

ember-concurrency is an amazing tool for managing asynchronous tasks in Ember applications. Until recently, the TypeScript story for ember-concurrency has not been great with no official types, problems with type-safety, and reliance on a proof-of-concept only available in an alpha release. Recent work by Godfrey Chan and the author, with help from Max Fierke, Chris Krycho, and Dan Freeman, has greatly improved the experience of using ember-concurrency with TypeScript. This article will attempt to summarize this work, demonstrate what is now possible, and suggest some best practices.

ember-concurrency

Traditionally, ember-concurrency tasks would be defined in this way:

import { task } from 'ember-concurrency';

export default Component.extend({
  myTask: task(function*(bar) {
    const result = yield this.foo();
    return result ? result : bar;
  }).restartable(),
  
  // ⋮
}

Ember Octane introduced using native JavaScript classes to define controllers and components (among other things), where we typically find ember-concurrency tasks. TypeScript users have been using native classes in Ember for some time (it's been possible to use native classes in Ember for a while, but Octane made it officially supported and the default). There's a problem with using ember-concurrency the traditional way in native classes, though. Because tasks are computed properties, we can't just assign them to a class field. This doesn't work:

import { task } from 'ember-concurrency';

export default class MyComponent extends Component {
  myTask = task(function*(this: MyComponent, bar: string) {
    const result = yield this.foo();
    return result ? result : bar;
  });
  
  // ⋮
}

The problem is that ember-concurrency tasks, like all computed properties, must be defined on the class's prototype. Computed properties solve this by using a decorator (the computed export from @ember/object can be used both to define a computed property the classic way and to decorate a native getter). Fortunately, the ember-concurrency-decorators package was created to make this work for ember-concurrency.

ember-concurrency-decorators

ember-concurrency-decorators provides several decorators that can be used to decorate a generator method and turn it into an ember-concurrency task, e.g.

import { task } from 'ember-concurrency-decorators';

export default class MyComponent extends Component {
  @task
  *myTask(this: MyComponent, bar: string) {
    const result = yield this.foo();
    return result ? result : bar;
  };
  
  // ⋮
}

Great! Problem solved! But unfortunately, not for TypeScript. The problem is that decorators cannot change the type of the thing they decorate. As far as TypeScript is concerned, myTask is a generator function and therefore an expression like

this.myTask.perform();

is invalid because a generator function does not have a perform method.

Jan Buschtöns and I worked on a proof-of-concept for solving this issue that allows you write to something like:

import { task } from 'ember-concurrency-decorators';

export default class MyComponent extends Component {
  @task
  myTask = task(function*(this: MyComponent, bar: string) {
    const result = yield this.foo();
    return result ? result : bar;
  });
  
  // ⋮
}

Here task wraps the generator function and simply passes it through when used in this way. The task wrapper is inert at runtime, but it changes the type from a generator function to an ember-concurrency task such that it can be .perform()ed, etc. Jan even released this as an alpha version and some people have successfully used it in production apps. This approach has a few issues though. The usage of task as both a decorator and a wrapping utility function provides the convenience of a single import, but can be a bit confusing. Furthermore, the maintainers of ember-concurrency are hoping to eventually merge ember-concurrency-decorators into ember-concurrency such that the task import from ember-concurrency can also be used as a decorator. This is incompatible with the dual-usage of the task import because they want to continue to support the use of task() to define a task in Ember objects and other places where a decorator is inappropriate. Finally, the proof-of-concept introduced type definitions for ember-concurrency, but they had to be manually imported because they did not live in the ember-concurrency package.

A good bit of time passed where the alpha-release of ember-concurrency-decorators was really the only available solution that would give you some type-safety without jumping through hoops. Thankfully, Godfrey Chan tackled the problem and took the lead in getting official type definitions into ember-concurrency, creating ember-concurrency-ts, and creating ember-concurrency-async, which is not required for using TypeScript with ember-concurrency, but gives you better type-safety and has other advantages.

ember-concurrency-ts

The ember-concurrency-ts package provides a couple of utility functions for using TypeScript with ember-concurrency. The main function is taskFor(), which can be used one of two ways.

The first usage is as a way to access decorated task properties that changes the type from a generator function to an ember-concurrency task:

import { task } from 'ember-concurrency-decorators';
import { taskFor } from 'ember-concurrency-ts';

export default class MyComponent extends Component {
  @task
  *myTask(bar: string) {
    const result = yield this.foo();
    return result ? result : bar;
  }
  
  foo(): Promise<string | null> {
    // ⋮
  }
  
  get lastValue() {
    return taskFor(this.myTask).last.value;
  }
  
  @action
  doSomething(bar: string) {
    return taskFor(this.myTask).perform(bar);
  }
 }

There also exists a perform() utility function that is a shortcut for, e.g. taskFor(this.myTask).perform(bar). It could have been used above:

import { perform } from 'ember-concurrency-ts';

export default class MyComponent extends Component {
  // ⋮
  
  @action
  doSomething(bar: string) {
    return perform(this.myTask, bar);
  }
}

The second usage works similarly, but you use taskFor() at assignment when defining the task:

import { task } from 'ember-concurrency-decorators';
import { taskFor } from 'ember-concurrency-ts';

export default class MyComponent extends Component { 
  @task
  myTask = taskFor(function*(this: MyComponent, bar: string) {
    const result = yield this.foo;
    return result ? result : bar;
  });
  
  foo(): Promise<string | null> {
    // ⋮
  }
  
  get lastValue() {
    return this.myTask.last.value;
  }
  
  @action
  doSomething(bar: string) {
    return this.myTask.perform(bar);
  }
 }

This has the tradeoff of a slightly more complex task definition, but then you don't need to use a utility function to access the task. While both are perfectly acceptable, this author prefers the latter because it only requires a single usage of taskFor when defining the task and nothing extra when performing or accessing the task. There is a minor annoyance of having to type this if the task references this, but this can be eliminated with ember-concurrency-async (and you get better type-safety!).

ember-concurrency-async

The ember-concurrency-async package provides a Babel transform that allows you to define ember-concurrency tasks using async/await rather than generator functions:

import { task } from 'ember-concurrency-decorators';

export default class MyComponent extends Component {
  @task
  async myTask(bar: string) {
    const result = await this.foo();     
    return result ? result : bar;
  }
  
  foo(): Promise<string | null> {
    // ⋮
  }
  
  // ⋮
}

In addition to this syntax being more familiar to many JavaScript developers, it has some special advantages for TypeScript. First, generator functions are not well supported in TypeScript and cannot be made fully type-safe. As of now, all type information is lost through a yield because it always returns type any. In the examples in previous sections that use generator functions, the type of result is any. When using await, however, type information is preserved because await returns the resolved type of the promise it awaits. In the example above, the type of result is string | null. The second advantage is that, when using the taskFor utility from ember-concurrency-ts at assignment, you can use an async arrow function, eliminating the need to type this:

import { task } from 'ember-concurrency-decorators';
import { taskFor } from 'ember-concurrency-ts';

export default class MyComponent extends Component {
  @task
  myTask = taskFor(async (bar: string) => {
    const result = await this.foo();
    return result ? result : bar;
  });
  
  // ⋮ 
 }

This simplifies using taskFor at assignment while providing complete type-safety for ember-concurrency tasks! One note is that, currently, you will need to import a couple of additional type definitions. I'll also point out that the transform converts async arrow functions to non-arrow generator functions (arrow generator functions have been proposed but no Babel plugin exists for them at this time). The this context is bound, however, by the @task decorator, so this inside the async function will still refer to the containing class at runtime.

Summary

The ember-concurrency, ember-concurrency-decorators, ember-concurrency-ts, and ember-concurrency-async packages can be used together to provide a good experience using ember-concurrency with TypeScript. At some point in the future, some or all of these packages may be merged into the ember-concurrency package making the experience even nicer.

Here is documentation for each of these packages with regard to TypeScript:

I hope this has been helpful in demonstrating how to use ember-concurrency with TypeScript and the options available today. If you have any questions or corrections, please find me on the the Ember Community Discord where my handle is jamescdavis.

5f08d0a27d37200001987022
Extensions
Using Yalc for Ember Engine/addon Development
emberember.jsemberjsember-enginesyalcnodemonworkflowDX
When developing an Ember Engine (or other addon), you'll often want to test in a consuming host app that rebuilds when you make changes to the engine/addon. Yalc will let you do this in a way that handles dependency resolution and other concerns.
Show full content

When developing an Ember Engine (or other addon), you'll often want to test in a consuming host app that rebuilds when you make changes to the engine/addon. One way to do this is with yarn link (or npm link if using npm), which creates a symlink in the host app's node_modules directly to the engine/addon's development directory. This works pretty well, except when it comes to dependencies (among other issues). Yarn will not attempt to resolve the dependencies of a package linked with yarn link (nor npm with npm link). ?

Fortunately Yalc exists, which uses a local repository instead of symlinks. Another advantage of Yalc, is that is will simulate an actual publish, running any publish-related hooks your engine/addon may have (e.g. prepublish), giving you a closer-to-prod experience without having to actually publish.

To install Yalc, run:

yarn global add yalc

or, if using npm:

npm install -g yalc

or, better yet, with Volta (highly recommended):

volta install yalc

With Yalc installed, in the engine/addon directory, run:

yalc publish

which will publish the engine/addon to the local Yalc package repository.

Next, go to your consuming app's directory and run:

yalc add my-engine-or-addon-name

You've now added the development version of your engine/addon that you just published to your host app.

Next, run yarn to install dependencies:

yarn

You can now run your host app with:

ember serve

When you make an update to the engine/addon, in the engine/addon directory run:

yalc push

This will publish your changes and push them out automatically to any consuming apps that have yalc added the engine/addon. The consuming app should detect the changes and rebuild automatically.
Note: if you are using watchman, make sure you're not ignoring node_modules in your watchman config or watchman will fail to pick up changes. If you need to continue to ignore node_modules for some reason, you can work around this by using yalc add --link when you add the engine/addon to the host app.

Automating Pushes with Nodemon

You likely want changes to your engine/addon to be automatically pushed out to the consuming app so that the workflow is similar to what you get with yarn link. While Yalc does not have a watch mode, you can use Nodemon to do this.

First, install Nodemon globally with one of:

volta install nodemon

yarn add global nodemon

npm install -g nodemon

Now, in the engine/addon directory, run Nodemon and tell it to yalc push:

nodemon -e js,ts,hbs,css,scss -x yalc push

Note: We have to tell Nodemon to watch .js, .ts, .hbs, and .(s)css files because it only watches .js files (and a few others) by default.

Leave that running and it will automatically run yalc push on each change!

Note: if you have installed Yalc with Volta, you may have to specify the path to yalc. IMHO, the best way to do this is automatically using which:

nodemon -e js,ts,hbs,css,scss -x `which yalc` push

I recommend creating an alias for this. I use:

alias yalc-watch="nodemon -e js,ts,hbs,css,scss -x `which yalc` push"

and then I can just run:

yalc-watch

I hope you found this useful for setting up your Ember Engine/addon development workflow!

TL;DR?
  1. install yalc and nodemon globally
  2. in engine/addon: yalc publish
  3. in app: yalc add my-engine-or-addon
  4. in engine/addon: nodemon -e js,ts,hbs -x yalc push
5ed13bec7d37200001986e48
Extensions
Angle Bracket Invocation in Ember.js
emberember.jsemberjsember octaneoctaneangle bracket invocationglimmercomponents

As long as components have been a thing in Ember.js the standard syntax for invoking them in a handlebar template has been using double curly braces, e.g.

{{my-component}}
Show full content

As long as components have been a thing in Ember.js the standard syntax for invoking them in a handlebar template has been using double curly braces, e.g.

{{my-component}}
5c4f07d233af280001c087b9
Extensions