Skip to content

Commit 9f013fd

Browse files
committed
README: add docs for string-keyed lookups.
1 parent 334ec9e commit 9f013fd

File tree

1 file changed

+75
-23
lines changed

1 file changed

+75
-23
lines changed

README.md

Lines changed: 75 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22

33
Use TypeScript in your Ember 2.x and 3.x apps!
44

5-
[![*nix build status (master)](https://travis-ci.org/typed-ember/ember-cli-typescript.svg?branch=master)](https://travis-ci.org/typed-ember/ember-cli-typescript)
6-
[![Windows build status](https://ci.appveyor.com/api/projects/status/i94uv7jgmrg022ho/branch/master?svg=true)](https://ci.appveyor.com/project/chriskrycho/ember-cli-typescript/branch/master)
7-
[![Ember Observer Score](https://emberobserver.com/badges/ember-cli-typescript.svg)](https://emberobserver.com/addons/ember-cli-typescript)
5+
[![*nix build status (master)](https://travis-ci.org/typed-ember/ember-cli-typescript.svg?branch=master)](https://travis-ci.org/typed-ember/ember-cli-typescript) [![Windows build status](https://ci.appveyor.com/api/projects/status/i94uv7jgmrg022ho/branch/master?svg=true)](https://ci.appveyor.com/project/chriskrycho/ember-cli-typescript/branch/master) [![Ember Observer Score](https://emberobserver.com/badges/ember-cli-typescript.svg)](https://emberobserver.com/addons/ember-cli-typescript)
86

97
* [Setup and Configuration](#setup-and-configuration)
108
* [Ember Support](#ember-support)
@@ -15,9 +13,12 @@ Use TypeScript in your Ember 2.x and 3.x apps!
1513
* [The `types` directory](#the-types-directory)
1614
* [Global types for your package](#global-types-for-your-package)
1715
* [Environment configuration typings](#environment-configuration-typings)
18-
* [Service and controller injections](#service-and-controller-injections)
19-
* [Ember Data lookups](#ember-data-lookups)
20-
* [Opt-in unsafety](#opt-in-unsafety)
16+
* [String-keyed lookups](#string-keyed-lookups)
17+
* [`this` type workaround](#this-type-workaround)
18+
* [Nested keys in `get` or `set`](#nested-keys-in-get-or-set)
19+
* [Service and controller injections](#service-and-controller-injections)
20+
* [Ember Data lookups](#ember-data-lookups)
21+
* [Opt-in unsafety](#opt-in-unsafety)
2122
* [Type definitions outside `node_modules/@types`](#type-definitions-outside-node_modulestypes)
2223
* [ember-browserify](#ember-browserify)
2324
* ["TypeScript is complaining about multiple copies of the same types"](#typescript-is-complaining-about-multiple-copies-of-the-same-types)
@@ -83,9 +84,7 @@ However, there are a few things worth noting if you're already familiar with Typ
8384

8485
## Using TypeScript with Ember effectively
8586

86-
In addition to the points made below, you may find the "Update" sequence in the [Typing Your Ember][typing-your-ember] blog series particularly helpful in knowing how to do specific things. In particular, [Update, Part 4][pt4] is a really important guide to making the service and controller injections and Ember Data lookups behave as described below.
87-
88-
[pt4]: http://www.chriskrycho.com/2018/typing-your-ember-update-part-4.html
87+
In addition to the points made below, you may find the [Typing Your Ember][typing-your-ember] blog series (especially the "Update" sequence) particularly helpful in knowing how to do specific things.
8988

9089
### Incremental adoption
9190

@@ -134,7 +133,66 @@ Along with the @types/ files mentioned above, ember-cli-typescript adds a starte
134133

135134
We install this file because the actual `config/environment.js` is (a) not actually identical with the types as you inherit them in the content of an application, but rather a superset of what an application has access to, and (b) not in a the same location as the path at which you look it up. We map it to the lookup path within your `types` directory, and TypeScript resolves it correctly.
136135

137-
### Service and controller injections
136+
### String-keyed lookups
137+
138+
Ember makes heavy use of string-based APIs to allow for a high degree of dynamicism. With some limitations, you can nonetheless use TypeScript very effectively to get auto-complete/IntelliSense as well as to accurately type-check your applications.
139+
140+
The "Update" sequence in the Typing Your Ember has detailed explanations and guides for getting good type-safety for Ember's string-based APIs, e.g. the use of `get` and `set`, service and controller injection, Ember Data models and lookups
141+
142+
* [Part 1][pt1]: A look at normal Ember objects, "arguments" to components (and controllers), and service (or controller) injections.
143+
* [Part 2][pt2]: Class properties — some notes on how things differ from the `Ember.Object` world.
144+
* [Part 3][pt3]: Computed properties, actions, mixins, and class methods.
145+
* [Part 4][pt4]: Using Ember Data, and service and controller injections improvements. (This includes a detailed guide to updating making the service and controller injections and Ember Data lookups behave as described below.)
146+
147+
[pt1]: http://www.chriskrycho.com/2018/typing-your-ember-update-part-1.html
148+
[pt2]: http://www.chriskrycho.com/2018/typing-your-ember-update-part-2.html
149+
[pt3]: http://www.chriskrycho.com/2018/typing-your-ember-update-part-3.html
150+
[pt4]: http://www.chriskrycho.com/2018/typing-your-ember-update-part-4.html
151+
152+
A few of the most common speed-bumps are listed here to help make this easier:
153+
154+
#### `this` type workaround
155+
156+
One important note for using `class` types effectively with today's Ember typings: you will (at least for now) need to explicitly write out a `this` type for methods, computed property callbacks, and actions if you are going to use `get` or `set`
157+
158+
```ts
159+
import Component from '@ember/component';
160+
161+
export default class UserProfile extends Component {
162+
changeUsername(this: UserProfile, userName: string) {
163+
// ^---------------^
164+
// `this` tells TS to use `UserProfile` for `get` and `set` lookups
165+
}
166+
}
167+
```
168+
169+
This is a workaround for how incredibly dynamic `Ember.Object` instances are, and is only necessary but i; again, see [the relevant blog post for details][pt2].
170+
171+
#### Nested keys in `get` or `set`
172+
173+
In general, `this.get` and `this.set` will work as you'd expect _if_ you're doing lookups only a single layer deep. Things like `this.get('a.b.c')` don't (and can't ever!) type-check; see the blog posts for a more detailed discussion of why.
174+
175+
The workaround is simply to do one of two things:
176+
177+
1. **The type-safe approach.** This _will_ typecheck, but is both ugly and only works \*if there are no `null`s or `undefined`s along the way. If `nested` is `null` at runtime, this will crash!
178+
179+
```ts
180+
import { get } from '@ember/object';
181+
182+
// -- Type-safe but ugly --//
183+
get(get(get(someObject, 'deeply'), 'nested'), 'key');
184+
```
185+
186+
2. **Using `// @ts-ignore`.** This will _not do any type-checking_, but is useful for the cases where you are intentionally checking a path which may be `null` or `undefined` anywhere long it.
187+
188+
```ts
189+
// @ts-ignore
190+
get(someObject, 'deeply.nested.key');
191+
```
192+
193+
It's usually best to include an explanation of _why_ you're ignoring a lookup!
194+
195+
#### Service and controller injections
138196

139197
Ember does service and controller lookups with the `inject` helpers at runtime, using the name of the service or controller being injected up as the default value—a clever bit of metaprogramming that makes for a nice developer experience. TypeScript cannot do this, because the name of the service or controller to inject isn't available at compile time in the same way. This means that if you do things the normal Ember way, you will have to specify the type of your service or controller explicitly everywhere you use it.
140198

@@ -221,7 +279,7 @@ You'll need to add that module and interface declaration to all your existing se
221279

222280
If you have a reason to fall back to just getting the `Service` or `Controller` types, you can always do so by just using the string-less variant: `service('session')` will check that the string is a valid name of a service; `session()` will not.
223281

224-
### Ember Data lookups
282+
#### Ember Data lookups
225283

226284
The same basic approach is in play for Ember Data lookups. As a result, once you add the module and interface definitions for each model, serializer, and adapter in your app, you will automatically get type-checking and autocompletion and the correct return types for functions like `findRecord`, `queryRecord`, `adapterFor`, `serializerFor`, etc. No need to try to write out those (admittedly kind of hairy!) types; just write your Ember Data calls like normal and everything _should_ just work.
227285

@@ -279,7 +337,7 @@ In addition to the registry, note the oddly defined class for `DS.Model`s. This
279337

280338
[pt2]: http://www.chriskrycho.com/2018/typing-your-ember-update-part-2.html
281339

282-
#### Opt-in unsafety
340+
##### Opt-in unsafety
283341

284342
Also notice that unlike with service and controller injections, there is no unsafe fallback method by default, because there isn't an argument-less variant of the functions to use as there is for `Service` and `Controller` injection. If for some reason you want to opt _out_ of the full type-safe lookup for the strings you pass into methods like `findRecord`, `adapterFor`, and `serializerFor`, you can add these declarations somewhere in your project:
285343

@@ -303,7 +361,7 @@ declare module 'ember-data' {
303361

304362
However, we **_strongly_** recommend that you simply take the time to add the few lines of declarations to each of your `DS.Model`, `DS.Adapter`, and `DS.Serializer` instances instead. It will save you time in even the short run!
305363

306-
#### Fixing the Ember Data `error TS2344` problem
364+
##### Fixing the Ember Data `error TS2344` problem
307365

308366
If you're developing an Ember app or addon and _not_ using Ember Data (and accordingly not even have the Ember Data types installed), you may see an error like this and be confused:
309367

@@ -450,25 +508,19 @@ Here is the short list of things which do _not_ work yet in the version of the t
450508

451509
### Some `import`s don't resolve
452510

453-
You'll frequently see errors for imports which TypeScript doesn't know how to
454-
resolve. For example, if you use Ember Concurrency today and try to import its
455-
`task` helper:
511+
You'll frequently see errors for imports which TypeScript doesn't know how to resolve. For example, if you use Ember Concurrency today and try to import its `task` helper:
456512

457513
```typescript
458514
import { task } from 'ember-concurrency';
459515
```
460516

461-
You'll see an error, because there aren't yet type definitions for it. You may
462-
see the same with some addons as well. **These won't stop the build from
463-
working;** they just mean TypeScript doesn't know where to find those.
517+
You'll see an error, because there aren't yet type definitions for it. You may see the same with some addons as well. **These won't stop the build from working;** they just mean TypeScript doesn't know where to find those.
464518

465-
Writing these missing type definitions is a great way to pitch in! Jump in
466-
\#topic-typescript on the Ember Slack and we'll be happy to help you.
519+
Writing these missing type definitions is a great way to pitch in! Jump in \#topic-typescript on the Ember Slack and we'll be happy to help you.
467520

468521
### Type safety when invoking actions
469522

470-
TypeScript won't detect a mismatch between this action and the corresponding
471-
call in the template:
523+
TypeScript won't detect a mismatch between this action and the corresponding call in the template:
472524

473525
```typescript
474526
Ember.Component.extend({

0 commit comments

Comments
 (0)