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

Component helper with {{#let}} not playing well with components that accept and yield generics #575

Open
vstefanovic97 opened this issue May 16, 2023 · 2 comments
Labels
documentation Improvements or additions to documentation typescript limitation Something we can't do in the system as it stands

Comments

@vstefanovic97
Copy link

vstefanovic97 commented May 16, 2023

Detailed reproduction can be found in this branch where I added the cases here

Files to check are reproduction.${ts|hbs} and options$.{ts|hbs}

Short summary
Let's say we have following file

// options.ts
import Component from "@glimmer/component";

interface OptionsComponentSignature<T> {
    Args: {
        options: T[];
    }
    Blocks: {
        default: [options: T]
    }
}

export default class OptionsComponent<T> extends Component<OptionsComponentSignature<T>> {} 

When trying to use it like this

// reproduction.hbs
{{#let (component "options" options=(array 1 2 3)) as |OptionsComponent|}}
  <OptionsComponent as |option|> // option is of type unknown, where actually it should be of type number
    {{option}}
  </OptionsComponent>
{{/let}}
@vstefanovic97
Copy link
Author

vstefanovic97 commented May 18, 2023

After further inspection this problem only seems to happen when passing in a component name as a string to {{component}} helper, if we pass in a component class it works just fine.

After some digging around glint type it seems that when we pass in just a string name it uses this type resolveForBind, and that is causing trouble for -bind-invokable to infer the type.

When passing in a component class, the type being used is resolveForBind which works just fine, I just can't spot the difference in why this one is working whereas the first one is not

@dfreeman dfreeman added documentation Improvements or additions to documentation typescript limitation Something we can't do in the system as it stands labels May 19, 2023
@dfreeman
Copy link
Member

Unfortunately this is a limitation of what TypeScript is able to do with classes and functions that have type parameters in their signatures. It's not possible purely within the type system itself to preserve generics like this; the only way it's possible to preserve them is in very specific syntactic cases involving runtime function calls.

Glint is designed to use those exact forms to trigger the preservation in TypeScript wherever possible, but unfortunately the dynamicity of string lookups with {{component}} makes that impossible. When resolveForBind receives an actual component (or modifier/helper) value, we're able to preserve the generics, but starting from a string value is one step too far removed from the actual value.

If you're targeting any version of Ember >= 3.27, I'd strongly recommend beginning to migrate away from string lookups with {{component}}/{{modifier}}/{{helper}}. Aside from avoiding this issue, it will also make it easier for you to move to first-class <template> syntax in the future, as string-based lookups are unavailable in strict mode.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation typescript limitation Something we can't do in the system as it stands
Projects
None yet
Development

No branches or pull requests

2 participants