glint
glint copied to clipboard
Infer route model when controller exists
I was wondering why I was getting Glint errors due to @model being unknown in a route template. After reading https://typed-ember.gitbook.io/glint/using-glint/ember/routes-and-controllers it seems that this is indeed expected, as I have a controller and have not declared the model type there.
After adding something like declare model: Awaited<ReturnType<MyImportedRoute['model']>>; it makes it work!
However I was asking myself if this boilerplate is really needed? Couldn't Glint infer that type automatically, just as it seems to be able to do when no controller exists?
Ultimately this comes down to a design restriction in Glint: a given template only ever has at most 1 backing module, so if a controller exists, we can't then also pull type information from the route. Of course we could loosen that restriction in Glint's internals, but doing so would have the potential to be a pretty complex (and performance intensive) change, as it would essentially mean making the core able to treat arbitrarily-many independent files on disk as somehow composing a single logical module.
Because this only really comes up in this specific situation with route + controller pairs, and because the overall model for routing in Ember is under consideration to be reworked in the near(ish?) future, we haven't gone that route in Glint (pun mostly unintended ð)
We have had some fleeting conversations in the past about paving the road a bit better in the Ember types for defining a controller's model field, with potentially some subset/combination of:
- making
Controller<T>parametric on the type of the model field - making
Controller<T>parametric on the type of the corresponding route - exposing a
ModelForRoute<T>utility type that abstracts away some of theAwaited<ReturnType<T['model']>>boilerplate
But I don't think anyone had strong enough feelings about what the best path forward there was to make it happen.
One reason that we haven't pushed forward on that is: you can provide a default type parameter for what a Controller<T> is, but there's no way to constrain it to be correct without some manual wiring, courtesy of the convention-based flow that routes and controllers use. And it gets worse: if you happen to use setupController, then using a ModelForRoute utility to define the model prop on the Controller results in circularity in the definitions (playground). ð©
Not that I'm necessarily endorsing this approach, but it does seem TS can resolve the circularity if it's plumbed through via a type parameter on the base Controller class https://tsplay.dev/WzaLkm
Oh, that's interesting.
âŠI still don't want it. ð