Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Future public @ember/engine types #19923

Merged
merged 3 commits into from
Feb 10, 2022
Merged

Conversation

wagenet
Copy link
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
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!

@@ -143,7 +143,7 @@ export default class Container {
@param {String} [options.source] The fullname of the request source (used for local lookup)
@return {any}
*/
lookup(fullName: string, options: LookupOptions): any {
lookup<T>(fullName: string, options?: TypeOptions): T | undefined {
Copy link
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
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
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
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.

@@ -417,7 +417,7 @@ export default class Registry implements IRegistry {

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
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
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
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
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
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
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
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
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
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;

Copy link
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: .

@@ -133,7 +133,7 @@ export default class CurlyComponentManager

if (layout === undefined) {
if (layoutName !== undefined) {
let _factory = owner.lookup<TemplateFactory>(`template:${layoutName}`);
let _factory = owner.lookup(`template:${layoutName}`) as TemplateFactory;
Copy link
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
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
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.

@@ -265,7 +265,7 @@ class ActionModifierManager implements InternalModifierManager<ActionState, obje
}

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
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
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
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
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
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.

@@ -312,11 +312,11 @@ class EmberRouter<R extends Route = Route> extends EmberObject.extend(Evented) i
this._resetQueuedQueryParameterChanges();
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
Contributor

Choose a reason for hiding this comment

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

~: as throughout.

@@ -44,7 +44,7 @@ interface View {
@param {Object} owner
*/
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
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.

@@ -19,15 +23,14 @@ expectTypeOf(
instance.register('service:store-singleton', Store, { singleton: true, instantiate: true })
).toEqualTypeOf<void>();

expectTypeOf(instance.lookup<Store>('service:store')).toEqualTypeOf<Store | undefined>();
expectTypeOf(instance.lookup('service:store')).toEqualTypeOf<unknown>();
Copy link
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. 🎉

@@ -20,7 +20,7 @@ import { expect } from '@glimmer/util';
@since 3.14.0
*/
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
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.

@@ -15,7 +16,7 @@ const ENGINE_PARENT = symbol('ENGINE_PARENT');
@static
@private
*/
export function getEngineParent(engine) {
export function getEngineParent(engine: EngineInstance): EngineInstance | undefined {
Copy link
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