Skip to content

Commit a28a285

Browse files
authored
Merge pull request emberjs#20521 from littleredridingfox/types/controller-registry-fix
[FEATURE] Add TypeScript support for looking up controllers in DI registry
2 parents 922480b + 8013003 commit a28a285

File tree

4 files changed

+56
-0
lines changed

4 files changed

+56
-0
lines changed

packages/@ember/controller/index.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,3 +366,29 @@ export function inject(
366366
}
367367

368368
export { Controller as default, ControllerMixin };
369+
370+
/**
371+
A type registry for Ember `Controller`s. Meant to be declaration-merged so string
372+
lookups resolve to the correct type.
373+
374+
Blueprints should include such a declaration merge for TypeScript:
375+
376+
```ts
377+
import Controller from '@ember/controller';
378+
379+
export default class ExampleController extends Controller {
380+
// ...
381+
}
382+
383+
declare module '@ember/controller' {
384+
export interface Registry {
385+
example: ExampleController;
386+
}
387+
}
388+
```
389+
390+
Then `@inject` can check that the service is registered correctly, and APIs
391+
like `owner.lookup('controller:example')` can return `ExampleController`.
392+
*/
393+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
394+
export interface Registry extends Record<string, Controller | undefined> {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// This module provides an 'extension' to the `@ember/owner` module from the
2+
// `@ember/controller` module. Our type publishing infrastructure will pass it
3+
// through unchanged, so end users will get this extension.
4+
5+
import type { Registry } from '@ember/controller';
6+
7+
declare module '@ember/owner' {
8+
export interface DIRegistry {
9+
controller: Registry;
10+
}
11+
}

packages/@ember/controller/type-tests/index.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,14 @@ expectTypeOf(inject()).toMatchTypeOf<PropertyDecorator>();
7272

7373
// @ts-expect-error Doesn't allow invalid types
7474
inject(1);
75+
76+
class ExampleController extends Controller {}
77+
78+
declare module '@ember/controller' {
79+
export interface Registry {
80+
example: ExampleController;
81+
}
82+
}
83+
84+
expectTypeOf(owner.lookup('controller:example')).toEqualTypeOf<ExampleController>();
85+
expectTypeOf(owner.lookup('controller:non-registered')).toEqualTypeOf<Controller | undefined>();

type-tests/@ember/controller-test/octane.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import Controller, { ControllerQueryParam, inject } from '@ember/controller';
2+
import { expectTypeOf } from 'expect-type';
3+
import type Owner from '@ember/owner';
24

35
class FirstController extends Controller {
46
foo = 'bar';
@@ -36,3 +38,9 @@ declare module '@ember/controller' {
3638
second: InstanceType<typeof SecondController>;
3739
}
3840
}
41+
42+
const owner = {} as Owner;
43+
44+
expectTypeOf(owner.lookup('controller:first')).toEqualTypeOf<FirstController>();
45+
expectTypeOf(owner.lookup('controller:second')).toEqualTypeOf<InstanceType<typeof SecondController>>();
46+
expectTypeOf(owner.lookup('controller:non-registered')).toEqualTypeOf<Controller | undefined>();

0 commit comments

Comments
 (0)