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

Typing custom components #294

Closed
simonihmig opened this issue Apr 8, 2022 · 3 comments · Fixed by #562
Closed

Typing custom components #294

simonihmig opened this issue Apr 8, 2022 · 3 comments · Fixed by #562
Labels
question Further information is requested

Comments

@simonihmig
Copy link
Contributor

We use custom components (own component manager) that have a type signature different from Glimmer components (e.g. besides args they have a "context" type, and things like Element make no sense for them, as they are all "DOM-less"). Currently I have typed them basically as any (ComponentLike) in the registry, as Glint would naturally not be able to understand them correctly.

What would be needed to make Glint aware of their signature? I guess I would basically have to do, what https://github.com/typed-ember/glint/blob/main/packages/environment-ember-loose/glimmer-component/index.ts does for Glimmer components, right?

Only drawback I see is the use of private imports. Do you consider them stable enough to use them in this special case? Or any plans to support this use case with public APIs?

@dfreeman dfreeman added the question Further information is requested label Apr 11, 2022
@dfreeman
Copy link
Member

dfreeman commented Apr 11, 2022

Can you give a little more detail about how your custom components work? A simple example of what it looks like to define and invoke one of them would be ideal, if that's something you can share 🙂

In general it should be possible to define a custom component's external-facing interface (i.e. how it's allowed to be invoked in a template) using only public interfaces via some type gymnastics with ComponentLike.

For example...

If I had a custom component base class for components that were essentially a glorified facade for making a GET request to some endpoint, I might type it like:

class BaseFetcherComponent<Params, Payload> {
  // ...
}

setComponentManager(/*...*/, BaseFetcherComponent);
setComponentTemplate(/*...*/, BaseFetcherComponent);

// Declare how Glint should allow `BaseFetcherComponent` subclasses to be invoked
interface BaseFetcherComponent<Params, Payload> extends InstanceType<ComponentLike<{
  Element: null;
  Args: { params: Params };
  Blocks: {
    loading: [];
    error: [message: string];
    ready: [payload: Payload];
  };
}>> {}

That's a fairly contrived example, though, and I'm not sure how well it might actually provide any insight for what you're aiming to do.

The external interface for your custom components is only half the story, though. The other half is what the template for a given instance of your custom component sees (what is this? what do I get if I access @foo? etc), and that's only currently possible via the private [Context]: ... API, as you called out.

I think it's likely that there will someday be a public API for that, but currently we're keeping it private to preserve some amount of flexibility as the internal details may shift around a bit. That said, those types are fairly stable, and I think there's a reasonable chance we'd treat any meaningful change there as grounds for a breaking version bump.

@simonihmig
Copy link
Contributor Author

Can you give a little more detail about how your custom components work? A simple example of what it looks like to define and invoke one of them would be ideal, if that's something you can share

Sure , the library is actually OSS. This is the signature of such a component:
https://github.com/kaliber5/ember-ecsy-babylon/blob/master/addon/components/domless-glimmer.ts#L22-L25

It has a context (think of react context) - which is generally not accessible in the template - and args. Besides custom args, they all have a @parent arg, which is implicitly passed through an ugly AST transform, due to... reasons (long version: emberjs/rfcs#597). And as mentioned they don't have/need Element, and (usually) only a single default block. Besides that, they work much like Glimmer components, with regards to this, @foo etc.

As a first solution I will probably try to type them similar to the existing examples, even when using private APIs. When I have that working, we could have a second look at how this could be achieved with public APIs if possible...

@simonihmig
Copy link
Contributor Author

On second thought, I realize that your example using ComponentLike actually provides a path to achieving that it seems. Let me try this...

@dfreeman dfreeman linked a pull request Apr 25, 2023 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants