Skip to content

Commit 0d760c2

Browse files
authored
Merge pull request #935 from typed-ember/octane-docs
docs: Octane and much polish and clarification
2 parents bf62a06 + 7d6628a commit 0d760c2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+2115
-4931
lines changed

.eslintrc.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module.exports = {
77
sourceType: 'module',
88
},
99
plugins: ['ember', '@typescript-eslint', 'prettier'],
10-
extends: ['eslint:recommended', 'plugin:ember/recommended'],
10+
extends: ['eslint:recommended', 'plugin:ember/recommended', 'prettier/@typescript-eslint'],
1111
env: {
1212
browser: true,
1313
},

.gitbook.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
root: ./docs
2+
3+
structure:
4+
readme: ./index.md

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ ember install ember-decorators@^3.1.0 @ember-decorators/babel-transforms@^3.1.0
8989

9090
#### Update ember-decorators
9191

92-
Follow the same process of deduplication, reinstallation, and re-deduplication as described for ember-cli-babel above. This will get you the latest version of ember-decorators and, importantly, its @ember-decorators/babel-transforms dependency.
92+
If you're on a version of Ember before 3.10, follow the same process of deduplication, reinstallation, and re-deduplication as described for ember-cli-babel above for ember-decorators. This will get you the latest version of ember-decorators and, importantly, its @ember-decorators/babel-transforms dependency.
9393

9494
#### Update ember-cli-typescript
9595

config/addon-docs.js

Lines changed: 0 additions & 12 deletions
This file was deleted.

config/deploy.js

Lines changed: 0 additions & 29 deletions
This file was deleted.

docs/SUMMARY.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Table of contents
2+
3+
* [ember-cli-typescript](index.md)
4+
* [Installation](installation.md)
5+
* [Configuration](configuration.md)
6+
* [TypeScript and Ember](ts/README.md)
7+
* [Using TypeScript With Ember Effectively](ts/using-ts-effectively.md)
8+
* [Decorators](ts/decorators.md)
9+
* [Current limitations](ts/current-limitations.md)
10+
* [Building Addons in TypeScript](ts/with-addons.md)
11+
* [Understanding the `@types` Package Names](ts/package-names.md)
12+
* [Working With Ember](ember/README.md)
13+
* [Components](ember/components.md)
14+
* [Services](ember/services.md)
15+
* [Routes](ember/routes.md)
16+
* [Controllers](ember/controllers.md)
17+
* [Helpers](ember/helpers.md)
18+
* [Testing](ember/testing.md)
19+
* [Working With Ember Data](ember-data/README.md)
20+
* [Models](ember-data/models.md)
21+
* [Cookbook](cookbook/README.md)
22+
* [Working with route models](cookbook/working-with-route-models.md)
23+
* [Working With Ember Classic](legacy/README.md)
24+
* [EmberComponent](legacy/ember-component.md)
25+
* [Mixins](legacy/mixins.md)
26+
* [Computed Properties](legacy/computed-properties.md)
27+
* [EmberObject](legacy/ember-object.md)
28+
* [Upgrading from 1.x](upgrade-notes.md)
29+
* [Troubleshooting](troubleshooting/README.md)
30+
* [Conflicting Type Dependencies](troubleshooting/conflicting-types.md)
31+

tests/dummy/app/templates/docs/configuration.md renamed to docs/configuration.md

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,42 @@
1-
# Configuring ember-cli-typescript
1+
# Configuration
22

33
## Blueprints
44

5-
By default, ember-cli-typescript installs the [ember-cli-typescript-blueprints][blueprints] package so that you can use Ember's generators like normal, but with all the special sauce you need for things to work nicely throughout your system with TypeScript.
5+
By default, ember-cli-typescript installs the [ember-cli-typescript-blueprints](https://github.com/typed-ember/ember-cli-typescript-blueprints) package so that you can use Ember's generators like normal, but with all the special sauce you need for things to work nicely throughout your system with TypeScript.
66

7-
[blueprints]: https://github.com/typed-ember/ember-cli-typescript-blueprints
8-
9-
If you want to stick with the normal JavaScript blueprints—say, because your team isn't ready to dive into the deep end with making *everything* TypeScript yet—you can simply uninstall the blueprints package.
7+
If you want to stick with the normal JavaScript blueprints—say, because your team isn't ready to dive into the deep end with making _everything_ TypeScript yet—you can simply uninstall the blueprints package.
108

119
With yarn:
1210

13-
```sh
11+
```bash
1412
yarn remove ember-cli-typescript-blueprints
1513
```
1614

1715
With npm:
1816

19-
```sh
17+
```bash
2018
npm uninstall ember-cli-typescript-blueprints
2119
```
2220

2321
## `tsconfig.json`
2422

25-
We generate a good default [`tsconfig.json`][blueprint], which will usually make everything _Just Work™_. In general, you may customize your TypeScript build process as usual using the `tsconfig.json` file.
23+
We generate a good default [`tsconfig.json`](https://github.com/typed-ember/ember-cli-typescript/blob/master/blueprints/ember-cli-typescript/files/tsconfig.json), which will usually make everything _Just Work™_. In general, you may customize your TypeScript build process as usual using the `tsconfig.json` file.
2624

2725
However, there are a few things worth noting if you're already familiar with TypeScript and looking to make further or more advanced customizations (but _most_ users can just ignore this section!):
2826

2927
1. The generated tsconfig file does not set `"outDir"` and sets `"noEmit"` to `true`. The default configuration we generate allows you to run editors which use the compiler without creating extraneous `.js` files throughout your codebase, leaving the compilation to ember-cli-typescript to manage.
3028

31-
You _can_ still customize those properties in `tsconfig.json` if your use case requires it, however. For example, to see the output of the compilation in a separate folder you are welcome to set `"outDir"` to some path and set `"noEmit"` to `false`. Then tools which use the TypeScript compiler (e.g. the watcher tooling in JetBrains IDEs) will generate files at that location, while the Ember.js/[Broccoli] pipeline will continue to use its own temp folder.
29+
You _can_ still customize those properties in `tsconfig.json` if your use case requires it, however. For example, to see the output of the compilation in a separate folder you are welcome to set `"outDir"` to some path and set `"noEmit"` to `false`. Then tools which use the TypeScript compiler (e.g. the watcher tooling in JetBrains IDEs) will generate files at that location, while the Ember.js/[Broccoli](http://broccolijs.com/) pipeline will continue to use its own temp folder.
3230

3331
2. Closely related to the previous point: any changes you do make to `outDir` won't have any effect on how _Ember_ builds your application—we run the entire build pipeline through Babel's TypeScript support instead of through the TypeScript compiler.
34-
35-
3. Since your application is built by Babel, and only *type-checked* by TypeScript, we set the `target` key in `tsconfig.json` to the current version of the ECMAScript standard so that type-checking uses the latest and greatest from the JavaScript standard library. The Babel configuration in your app's `config/targets.js` and any included polyfills will determine the final build output.
36-
37-
4. If you make changes to the paths included in or excluded from the build via your `tsconfig.json` (using the `"include"`, `"exclude"`, or `"files"` keys), you will need to restart the server to take the changes into account: ember-cli-typescript does not currently watch the `tsconfig.json` file. For more details, see [the TypeScript reference materials for `tsconfig.json`][tsconfig].
38-
39-
[blueprint]: https://github.com/typed-ember/ember-cli-typescript/blob/master/blueprints/ember-cli-typescript/files/tsconfig.json
40-
[Broccoli]: http://broccolijs.com/
41-
[tsconfig]: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html
32+
3. Since your application is built by Babel, and only _type-checked_ by TypeScript, we set the `target` key in `tsconfig.json` to the current version of the ECMAScript standard so that type-checking uses the latest and greatest from the JavaScript standard library. The Babel configuration in your app's `config/targets.js` and any included polyfills will determine the final build output.
33+
4. If you make changes to the paths included in or excluded from the build via your `tsconfig.json` (using the `"include"`, `"exclude"`, or `"files"` keys), you will need to restart the server to take the changes into account: ember-cli-typescript does not currently watch the `tsconfig.json` file. For more details, see [the TypeScript reference materials for `tsconfig.json`](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html).
4234

4335
## Enabling Sourcemaps
4436

4537
To enable TypeScript sourcemaps, you'll need to add the corresponding configuration for Babel to your `ember-cli-build.js` file:
4638

47-
```ts
39+
```typescript
4840
const app = new EmberApp(defaults, {
4941
babel: {
5042
sourceMaps: 'inline',

docs/cookbook/README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Cookbook
2+
3+
This “cookbook” section contains recipes for various scenarios you may encounter while working on your app or addon.
4+
5+
{% hint style="info" %}
6+
Have an idea for an item that should fit here? [Open an issue for it!](https://github.com/typed-ember/ember-cli-typescript/issues/new/choose) We'd love to help you help us make this experience more awesome for everyone.
7+
{% endhint %}
8+
9+
## Contents
10+
11+
* [Working with route models](./working-with-route-models.md)
12+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Working with route models
2+
3+
We often use routes’ models throughout our application, since they’re a core ingredient of our application’s data. As such, we want to make sure that we have good types for them!
4+
5+
We can start by defining some type utilities to let us get the resolved value returned by a route’s model hook:
6+
7+
```typescript
8+
import Route from '@ember/routing/route';
9+
10+
/**
11+
Get the resolved type of an item.
12+
13+
- If the item is a promise, the result will be the resolved value type
14+
- If the item is not a promise, the result will just be the type of the item
15+
*/
16+
export type Resolved<P> = P extends Promise<infer T> ? T : P;
17+
18+
/** Get the resolved model value from a route. */
19+
export type ModelFrom<R extends Route> = Resolved<ReturnType<R['model']>>;
20+
```
21+
22+
How that works:
23+
24+
* `Resolved<P>` says "if this is a promise, the type here is whatever the promise resolves to; otherwise, it's just the value"
25+
* `ReturnType<T>` gets the return value of a given function
26+
* `R['model']` \(where `R` has to be `Route` itself or a subclass\) uses TS's mapped types to say "the property named `model` on `R`
27+
28+
Putting those all together, `ModelFrom<Route>` ends up giving you the resolved value returned from the `model` hook for a given route:
29+
30+
```typescript
31+
type MyRouteModel = ModelFrom<MyRoute>;
32+
```
33+
34+
## `model` on the controller
35+
36+
We can use this functionality to guarantee that the `model` on a `Controller` is always exactly the type returned by `Route::model` by writing something like this:
37+
38+
```typescript
39+
import Controller from '@ember/controller';
40+
import MyRoute from '../routes/my-route';
41+
import { ModelFrom } from '../lib/type-utils';
42+
43+
export default class ControllerWithModel extends Controller {
44+
declare model: ModelFrom<MyRoute>;
45+
}
46+
```
47+
48+
Now, our controller’s `model` property will _always_ stay in sync with the corresponding route’s model hook.
49+
50+
**Note:** this _only_ works if you do not mutate the `model` in either the `afterModel` or `setupController` hooks on the route! That's generally considered to be a bad practice anyway. If you do change the type there, you'll need to define the type in some other way and make sure your route's model is defined another way.
51+

docs/ember-data/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Working With Ember Data
2+
3+
In this section, we cover how to use TypeScript effectively with specific Ember Data APIs \(anything you'd find under the `@ember-data` package namespace\).
4+
5+
We do _not_ cover general usage of Ember Data; instead, we assume that as background knowledge. Please see the Ember Data [Guides](https://guides.emberjs.com/release/models) and [API docs](https://api.emberjs.com/ember-data/release)!
6+

docs/ember-data/models.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Models
2+
3+
Ember Data models are normal TypeScript classes, but with properties decorated to define how the model represents an API resource and relationships to other resources. The decorators the library supplies "just work" with TypeScript at runtime, but require type annotations to be useful with TypeScript.
4+
5+
For an overview of using Ember's decorators with TypeScript, see our overview.
6+
7+
## `@attr`
8+
9+
The type returned by the `@attr` decorator is whatever [Transform](https://api.emberjs.com/ember-data/release/classes/Transform) is applied via the invocation.
10+
11+
* If you supply no argument to `@attr`, the value is passed through without transformation.
12+
* If you supply one of the built-in transforms, you will get back a corresponding type:
13+
* `@attr('string')``string`
14+
* `@attr(number)``number`,
15+
* `@attr('boolean')``boolean`
16+
* `@attr'date')``Date`
17+
* If you supply a custom transform, you will get back the type returned by your transform.
18+
19+
So, for example, you might write a class like this:
20+
21+
```typescript
22+
import Model, { attr } from '@ember-data/object';
23+
import CustomType from '../transforms/custom-transform';
24+
25+
export default class User extends Model {
26+
@attr()
27+
name?: string;
28+
29+
@attr('number')
30+
declare age: number;
31+
32+
@attr('boolean')
33+
declare isAdmin: boolean;
34+
35+
@attr('custom-transform')
36+
declare myCustomThing: CustomType;
37+
}
38+
```
39+
40+
**Very important:** Even more than with decorators in general, you should be careful when deciding whether to mark a property as optional `?` or definitely present \(no annotation\): Ember Data will default to leaving a property empty if it is not supplied by the API or by a developer when creating it. That is: the _default_ for Ember corresponds to an optional field on the model.
41+
42+
The _safest_ type you can write for an Ember Data model, therefore, leaves every property optional: this is how models _actually_ behave. If you choose to mark properties as definitely present by leaving off the `?`, you should take care to guarantee that this is a guarantee your API upholds, and that ever time you create a record from within the app, _you_ uphold those guarantees.
43+
44+
One way to make this safer is to supply a default value using the `defaultValue` on the options hash for the attribute:
45+
46+
```typescript
47+
import Model, { attr } from '@ember-data/object';
48+
49+
export default class User extends Model {
50+
@attr()
51+
declare name?: string;
52+
53+
@attr('number', { defaultValue: 13 })
54+
declare age: number;
55+
56+
@attr('boolean', { defaultValue: false })
57+
declare isAdmin: boolean;
58+
}
59+
```
60+
61+
## `@belongsTo`
62+
63+
The type returned by the `@hasMany` decorator depends on whether the relationship is `{ async: true }` \(which it is by default\).
64+
65+
* If the value is `true`, the type you should use is `DS.PromiseObject<Model>`, where `Model` is the type of the model you are creating a relationship to.
66+
* If the value is `false`, the type is `Model`, where `Model` is the type of the model you are creating a relationship to.
67+
68+
So, for example, you might define a class like this:
69+
70+
```typescript
71+
import Model, { belongsTo } from '@ember-data/model';
72+
import DS from 'ember-data'; // NOTE: this is a workaround, see discussion below!
73+
import User from './user';
74+
import Site from './site';
75+
76+
export default class Post extends Model {
77+
@belongsTo('user')
78+
declare user: DS.PromiseObject<User>;
79+
80+
@belongsTo('site', { async: false })
81+
declare site: Site;
82+
}
83+
```
84+
85+
These are _type_-safe to define as always present, that is to leave off the `?` optional marker:
86+
87+
* accessing an async relationship will always return a `PromiseObject`, which itself may or may not ultimately resolve to a value—depending on the API response—but will always be present itself.
88+
* accessing a non-async relationship which is known to be associated but has not been loaded will trigger an error, so all access to the property will be safe _if_ it resolves at all.
89+
90+
Note, however, that this type-safety is not a guarantee of there being no runtime error: you still need to uphold the contract for non-async relationships \(that is: loading the data first, or side-loading it with the request\) to avoid throwing an error!
91+
92+
## `@hasMany`
93+
94+
The type returned by the `@hasMany` decorator depends on whether the relationship is `{ async: true }` \(which it is by default\).
95+
96+
* If the value is `true`, the type you should use is `DS.PromiseManyArray<Model>`, where `Model` is the type of the model you are creating a relationship to.
97+
* If the value is `false`, the type is `EmberArray<Model>`, where `Model` is the type of the model you are creating a relationship to.
98+
99+
So, for example, you might define a class like this:
100+
101+
```typescript
102+
import Model, { hasMany } from '@ember-data/model';
103+
import EmberArray from '@ember/array';
104+
import DS from 'ember-data'; // NOTE: this is a workaround, see discussion below!
105+
import Comment from './comment';
106+
import User from './user';
107+
108+
export default class Thread extends Model {
109+
@hasMany('comment')
110+
declare comment: DS.PromiseManyArray<Comment>;
111+
112+
@hasMany('user', { async: false })
113+
declare participants: EmberArray<User>;
114+
}
115+
```
116+
117+
The same basic rules about the safety of these lookups as with `@belongsTo` apply to these types. The difference is just that in `@hasMany` the resulting types are _arrays_ rather than single objects.
118+
119+
## Importing `PromiseObject` and `PromiseManyArray`
120+
121+
There is no public import path in the [Ember Data Packages](https://emberjs.github.io/rfcs/0395-ember-data-packages.html) API for the `PromiseObject` and `PromiseManyArray` types. These types are slowly being disentangled from Ember Data and will eventually be removed. However, until they are, we need a way to refer to them. For _now_, the best option is to refer to them via the legacy `DS` import.
122+
123+
In the future, they will become unnecesary, as the types will simply be `Promise<Model>` and `Promise<Array<Model>>`.
124+

docs/ember/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Working With Ember
2+
3+
In this section, we cover how to use TypeScript effectively with specific Ember APIs \(anything you'd find under the `@ember` package namespace\).
4+
5+
We do _not_ cover general usage of Ember; instead, we assume that as background knowledge. Please see the Ember [Guides](https://guides.emberjs.com/release/) and [API docs](https://api.emberjs.com/ember/release)!
6+
7+
## Outline
8+
9+
* [Controllers](ember/controllers.md)
10+
* [Services](ember/services.md)
11+
* [Overview: Ember](ember/overview.md)
12+
* [Testing](ember/testing.md)
13+
* [Components](ember/components.md)
14+
* [Helpers](ember/helpers.md)
15+
* [Routes](ember/routes.md)

0 commit comments

Comments
 (0)