-
Notifications
You must be signed in to change notification settings - Fork 58
Simplify resolution and support plain function helpers in GlimmerX #54
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
Conversation
This temporarily breaks `on` and `fn` as they can no longer cleanly merge with their upstream declarations, which will force the issue we've been putting off in #25c
expectTypeOf(resolveOrReturn({} as never)).toEqualTypeOf<any>(); | ||
expectTypeOf(resolveOrReturn({} as never)).toEqualTypeOf<never>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, this is a nice win.
on: Invokable< | ||
<T extends keyof HTMLElementEventMap>( | ||
args: NoNamedArgs, | ||
event: T, | ||
callback: (event: HTMLElementEventMap[T]) => void | ||
) => CreatesModifier | ||
>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question for my illumination: how would we wire this up to a set of allowed events for a given DOM element (later, obviously; was just thinking about it when reading the change here)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I know TS's own DOM lib doesn't track that information, and with modifiers as generic as they are, I'm not sure if we could express that constraint in a way that would produce useful autosuggest for users even if the types were available.
For the slightly more general question of enforcing that modifiers are only applied to elements they'll work with, the way things are currently set up I think we'd wind up with something like:
function modifier<El extends Element, Positional extends unknown[], Named>(
f: (element: El, positional: Positional, named: Named): void | () => void
): new () => Invokable<(args: Named, ...positional: Positional) => CreatesModifier<Element>>
Overview
This change includes the necessary updates to our invokable resolution process to support treating plain functions as helpers in GlimmerX, ultimately simplifying baseline resolution down to two cases in the process.
Details
The key change is that we now never have free-floating function types being passed around in their Glint-internal format. Instead, invokables are always wrapped as wither
Invokable<T>
ornew (...) => Invokable<T>
, depending on the use case.The comment laid out in
packages/template/-private/resolution.d.ts
goes into further detail, but standaloneInvokable<T>
format allows us to resolve complex hand-declared items likefn
without losing type fidelity, whilenew () => Invokable<T>
is a natural fit for things like component classes that we already traffic heavily in.The
new
version also allows for the preservation of type parameters on the return side of functions that produce invokables, likehelper()
and{{component}}
. Previously we were using regular functions to support this case, which was what led to the difficulty of supporting plain functions.Notes
This notably breaks
fn
andon
for GlimmerX, which are now temporarily declared asfn2
andon2
to avoid clashing with the built-in definitions offn
andon
from GlimmerX itself. Previously we were able to get by with declaration merging, but the way we treat these values is now sufficiently diverged from the baseline definitions that that's no longer possible.This is already an issue we've been skirting as outlined in #25, and at this point I think the simplest path forward for the time being is to expose modules from the environment packages that directly reexport the same runtime values but with glint-compatible type declarations.
This will also give us the freedom to experiment with alternative form factors for e.g. component type parameters in a way that consumers can try out today rather than having to roll out breaking changes upstream first.