Skip to content

Generic types in yielded components #1020

Description

@forsner

It seems like there is an issue with how glint (ember-tsc?) figures out what type the yielded components have. It behaves differently depending on how the component is used.

Generic typed component:

interface SelectSignature<T> {
  Element: HTMLDivElement;
  Args: {
    options?: T[];
  };
}

class Select<T> extends Component<SelectSignature<T>> {
  <template>
    <div>
      {{#each @options as |option|}}
        <div>{{concat option ' value'}}</div>
      {{/each}}
    </div>
  </template>
}

Component that yields that component:

interface FieldSignature {
  Element: HTMLDivElement;
  Blocks: {
    default: [
      {
        Select: ComponentLike<SelectSignature<any>>;
      },
    ];
  };
}

class Field extends Component<FieldSignature> {
  <template>{{yield (hash Select=(component Select))}}</template>
}

And when using the Field component the types have different types based on how it is used.

With the intended usage:

class Usage extends Component {
  options = [
    { value: 'Option 1', test: 'Test 1' },
    { value: 'Option 2' },
    { value: 'Option 3' },
  ];

  <template>
    <Field as |field|>
      <field.Select @options={{this.options}} />
    </Field>
  </template>
}

In this example the @option will be options?: any[] | undefined.

Using it with {{component}}:

class Usage extends Component {
  options = [
    { value: 'Option 1', test: 'Test 1' },
    { value: 'Option 2' },
    { value: 'Option 3' },
  ];

  <template>
    <Field as |field|>
      {{component field.Select options=this.options}}
    </Field>
  </template>
}

In this example @option will be:

options: any[] & ({
        value: string;
        test: string;
    } | {
        value: string;
        test?: undefined;
    })[].

Using the Select on its own:

class Usage extends Component {
  options = [
    { value: 'Option 1', test: 'Test 1' },
    { value: 'Option 2' },
    { value: 'Option 3' },
  ];

  <template>
    <Select @options={{this.options}} />
  </template>
}

In the last case @option is:

options?: ({
          value: string;
          test: string;
      } | {
          value: string;
          test?: undefined;
      })[] | undefined

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions