Skip to content

Generic types in yielded components #1020

@forsner

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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions