diff --git a/type-tests/preview/@ember/owner-tests.ts b/type-tests/preview/@ember/owner-tests.ts index 15ad10010d0..83be87e1161 100644 --- a/type-tests/preview/@ember/owner-tests.ts +++ b/type-tests/preview/@ember/owner-tests.ts @@ -6,7 +6,11 @@ import Owner, { Resolver, KnownForTypeResult, } from '@ember/owner'; +import Component from '@glimmer/component'; import { expectTypeOf } from 'expect-type'; +// TODO: once we move the runtime export to `@ember/owner`, update this import +// as well. (That's why it's tested in this module!) +import { getOwner } from '@ember/application'; // Just a class we can construct in the Factory and FactoryManager tests declare class ConstructThis { @@ -143,6 +147,30 @@ typedStrings.map((aString) => owner.lookup(aString)); const aConstName = 'type:name'; expectTypeOf(owner.lookup(aConstName)).toBeUnknown(); +// Check handling with Glimmer components carrying a Signature: they should +// properly resolve to `Owner`, *not* `Owner | undefined`. +interface Sig { + Args: { + name: string; + age: number; + extra: T; + }; + Element: HTMLParagraphElement; + Blocks: { + default: [greeting: string]; + extra: [T]; + }; +} + +class ExampleComponent extends Component> { + checkThis() { + expectTypeOf(getOwner(this)).toEqualTypeOf(); + } +} + +declare let example: ExampleComponent; +expectTypeOf(getOwner(example)).toEqualTypeOf(); + // ----- Minimal further coverage for POJOs ----- // // `Factory` and `FactoryManager` don't have to deal in actual classes. :sigh: const Creatable = { diff --git a/types/preview/@ember/application/index.d.ts b/types/preview/@ember/application/index.d.ts index 9fe576425c9..0543ff53508 100644 --- a/types/preview/@ember/application/index.d.ts +++ b/types/preview/@ember/application/index.d.ts @@ -122,7 +122,13 @@ declare module '@ember/application' { // whenever we add new base classes to the framework. For example, if we // introduce a standalone `Service` or `Route` base class which *does not* // extend from `EmberObject`, it will need to be added here. - type FrameworkObject = EmberObject | GlimmerComponent; + // + // NOTE: we use `any` here because we need to make sure *not* to fix the + // actual GlimmerComponent type; using `unknown` or `{}` or `never` (the + // obvious alternatives here) results in a version which is too narrow, such + // that any subclass which applies a signature does not get resolved by the + // definition of `getOwner()` below. + type KnownFrameworkObject = EmberObject | GlimmerComponent; /** * Framework objects in an Ember application (components, services, routes, etc.) @@ -130,7 +136,15 @@ declare module '@ember/application' { * objects is the responsibility of an "owner", which handled its * instantiation and manages its lifetime. */ - export function getOwner(object: FrameworkObject): Owner; + // SAFETY: this first overload is, strictly speaking, *unsafe*. It is possible + // to do `let x = EmberObject.create(); getOwner(x);` and the result will *not* + // be `Owner` but instead `undefined`. However, that's quite unusual at this + // point, and more to the point we cannot actually distinguish a `Service` + // subclass from `EmberObject` at this point: `Service` subclasses `EmberObject` + // and adds nothing to it. Accordingly, if we want to catch `Service`s with this + // (and `getOwner(this)` for some service will definitely be defined!), it has + // to be this way. :sigh: + export function getOwner(object: KnownFrameworkObject): Owner; export function getOwner(object: unknown): Owner | undefined; /** * `setOwner` forces a new owner on a given object instance. This is primarily