Skip to content

Future public @ember/engine types#19923

Merged
wagenet merged 3 commits intoemberjs:masterfrom
wagenet:ember-engine-types
Feb 10, 2022
Merged

Future public @ember/engine types#19923
wagenet merged 3 commits intoemberjs:masterfrom
wagenet:ember-engine-types

Conversation

@wagenet
Copy link
Copy Markdown
Member

@wagenet wagenet commented Jan 26, 2022

No description provided.

@wagenet wagenet force-pushed the ember-engine-types branch 2 times, most recently from ea75656 to dd914f6 Compare February 2, 2022 19:53
@wagenet wagenet force-pushed the ember-engine-types branch 2 times, most recently from e34ab4a to cd198dd Compare February 7, 2022 18:17
Copy link
Copy Markdown
Contributor

@chriskrycho chriskrycho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will finish this review tomorrow, but WIP feedback!

@return {any}
*/
lookup(fullName: string, options: LookupOptions): any {
lookup<T>(fullName: string, options?: TypeOptions): T | undefined {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should actually return unknown, since it's impossible to infer from the arguments what the return type T would actually be.

Suggested change
lookup<T>(fullName: string, options?: TypeOptions): T | undefined {
lookup(fullName: string, options?: TypeOptions): unknown {

I know this matches the other implementations, but we should take care to move toward rather than away from safety with this work where our hands are not already tied in some way.

I expect you introduced this because there is client code which is relying on being able to freely cast because of the previous any return type?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense. I’ll give it a second look tomorrow.

Comment on lines +82 to +87
readonly registrations: { [key: string]: object | undefined };
_localLookupCache: { [key: string]: object | undefined };
readonly _normalizeCache: { [key: string]: string | undefined };
readonly _options: { [key: string]: TypeOptions | undefined };
readonly _resolveCache: { [key: string]: object | undefined };
readonly _typeOptions: { [key: string]: TypeOptions | undefined };
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Can we just use Record<string, TypeOptions> etc. here?
  2. It looks like this as well as the in check below are both because we don't yet have noUncheckedIndexAccess enabled, so we're sort of pushing that safety in manually?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do have noUncheckedIndexAccess now. I’ll revisit this.

getOptions(fullName: string): TypeOptions | undefined {
let normalizedName = this.normalize(fullName);
let options = this._options[normalizedName];
let options = normalizedName in this._options ? this._options[normalizedName] : undefined;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. This shouldn't be complaining about the access. 🤔 (See playground.) Since it's a string index, and given the type you wrote, it should correctly resolve it in either the Record or the index type form. What error was TS giving you here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what was happening before but it seems ok now.

owner: Owner,
options?: LookupOptions
): Option<Factory<{}, {}>> {
function componentFor(name: string, owner: Owner): Option<Factory<{}, {}>> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like an unrelated-to-types change – is this a dead code path or something?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type was incorrect. factoryFor doesn't take options.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Glad we caught it by doing this, then. Thanks! 😃

factoryFor<T, C>(fullName: string, options?: LookupOptions): Factory<T, C> | undefined;
register<T, C>(fullName: string, factory: Factory<T, C>, options?: LookupOptions): void;
hasRegistration(name: string, options?: LookupOptions): boolean;
lookup<T>(fullName: string, options?: TypeOptions): T | undefined;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As before, I'm fine if we leave this for now, but we should start working to eliminate it. 😩

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll fix it now.

}
declare const ContainerProxy: Mixin;

export { ContainerProxy as default };
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the normal export default form should work; does it not for some reason?

Suggested change
export { ContainerProxy as default };
export default ContainerProxy;

}
declare const RegistryProxyMixin: Mixin;

export { RegistryProxyMixin as default };
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export { RegistryProxyMixin as default };
export default RegistryProxyMixin;

@wagenet wagenet force-pushed the ember-engine-types branch from cd198dd to 47ff27b Compare February 9, 2022 01:59
Copy link
Copy Markdown
Contributor

@chriskrycho chriskrycho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guide:

Mark Meaning
? informational, non-blocking
~ not great, non-blocking
X blocking

Spoilers: no X, so :shipit: .

if (layout === undefined) {
if (layoutName !== undefined) {
let _factory = owner.lookup<TemplateFactory>(`template:${layoutName}`);
let _factory = owner.lookup(`template:${layoutName}`) as TemplateFactory;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?: Is this cast safe? If so, why? (I see that it was already here.)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know that it's safe, but it's how it was before so it probably is. I figure having as is safer (as in we know we're doing something potentially risky) than the way it was before where it wasn't clear that there was risk!

Comment on lines +713 to +714
if ((owner.lookup('-environment:main') as Environment)!.isInteractive) {
this.__dispatcher = owner.lookup('event_dispatcher:main') as EventDispatcher;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

~: Would be good to follow on with dev-time assertions instead of casting.


ensureEventSetup(actionState: ActionState): void {
let dispatcher = actionState.owner.lookup<EventDispatcher>('event_dispatcher:main');
let dispatcher = actionState.owner.lookup('event_dispatcher:main') as EventDispatcher;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?/~: as above re: casting and safety

let templateFullName = `template:components/${name}`;

return owner.lookup(templateFullName, options) || null;
return (owner.lookup(templateFullName, options) as Template) || null;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

~: Could use a safety comment here in the future.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, again I don't know that this is actually safe!

Comment on lines +176 to +178
const factory = owner.factoryFor(`helper:${name}`) as
| Factory<SimpleHelper, HelperFactory<SimpleHelper>>
| Factory<HelperInstance, HelperFactory<HelperInstance>>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?/~: as above on the cast.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, unsure this is safe, but it seems like it must be.

this.namespace = owner.lookup('application:main');

let bucketCache: BucketCache | undefined = owner.lookup(P`-bucket-cache:main`);
let bucketCache = owner.lookup(P`-bucket-cache:main`) as BucketCache | undefined;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

~: as throughout.

*/
export function getRootViews(owner: Owner): View[] {
let registry = owner.lookup<Dict<View>>('-view-registry:main')!;
let registry = owner.lookup('-view-registry:main') as Dict<View>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

~: as throughout, for reference.

).toEqualTypeOf<void>();

expectTypeOf(instance.lookup<Store>('service:store')).toEqualTypeOf<Store | undefined>();
expectTypeOf(instance.lookup('service:store')).toEqualTypeOf<unknown>();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we add that registry, these will (happily!) flag it up for us as a case where it no longer equals that type. 🎉

*/
export default function captureRenderTree(app: Owner): CapturedRenderNode[] {
let renderer = expect(app.lookup<Renderer>('renderer:-dom'), `BUG: owner is missing renderer`);
let renderer = expect(app.lookup('renderer:-dom') as Renderer, `BUG: owner is missing renderer`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

~: could use a better assertion to check that it's not just present but is the right/expected thing.

@private
*/
export function getEngineParent(engine) {
export function getEngineParent(engine: EngineInstance): EngineInstance | undefined {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💙

@wagenet wagenet merged commit a94cf49 into emberjs:master Feb 10, 2022
@wagenet wagenet deleted the ember-engine-types branch February 11, 2022 01:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants