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

Build breaks with certain property data types including generics and JSX.Element #350

Closed
1 task done
cjorasch opened this issue Dec 5, 2017 · 18 comments
Closed
1 task done
Assignees

Comments

@cjorasch
Copy link

cjorasch commented Dec 5, 2017

Stencil version:

 @stencil/core@0.0.8-10

I'm submitting a:

  • bug report

Current behavior:
The build breaks if you using certain property types in a component.

For example:

@Component({
    tag: 'formatted-list'
})
export class FormattedList<T = any> {

    // The list of values to be displayed
    @Prop() values: T[];

    // The function to transform a value to HTML
    @Prop() formatter?: ((value: T) => JSX.Element);

    // Delimiter between values
    @Prop() delim?: JSX.Element;

    render(): JSX.Element {
        const {values, formatter} = this;
        if (!values || !values.length) return null;
        const delim = this.delim || ', ';
        const result: JSX.Element[] = [];
        for (const value of values) {
            if (value != null) {
                if (result.length) result.push(delim);
                result.push(formatter ? formatter(value) : value.toString());
            }
        }
        return result;
    }
}

If you run nom start you get the following error:

[05:16.2]  @stencil/core v0.0.8-10
[05:16.2]  build, app, dev mode, started ...
[05:16.2]  compile started ...
[05:19.0]  compile finished in 2.85 s

[ ERROR ]  typescript: src/components.d.ts, line: 37
           Cannot find name 'T'.


[ ERROR ]  typescript: src/components.d.ts, line: 38
           Namespace 'global.JSX' has no exported member 'Element'.

These errors are related to the components.d.ts file where the attributes are defined:

  namespace JSXElements {
    export interface FormattedListAttributes extends HTMLAttributes {
      values?: T[];
      formatter?: ((value: T) => JSX.Element);
      delim?: JSX.Element;
    }
  }

Expected behavior:
It should be possible to use any valid TypeScript data types when creating components. This example is an attempt to create a simple reproducible case that illustrates the problems. I realize that it could be implemented differently to avoid the problems but the actual components I am working on are more involved.

The specific things I am trying to do in my components are:

  • Use generics to ensure that multiple related properties and methods have consistent data types. Changing from T[] to any[] would not be able to enforce the parameter type for the formatter function.
  • Use JSX.Element as a property value. This allows flexible values including strings or html. The error may also mean that other kinds of global types would also have problems.

Steps to reproduce:
The errors occur with version 0.0.8-10. When I revert to 0.0.8-8 the same errors exist in the components.d.ts file but at least I am able to start the application.

Potential approach:
One potential way to solve this problem would be to have the data type of the attributes reference the data type of the component class property by name. This is similar to how things like Partial<MyClass> work. For example:

  namespace JSXElements {
    export interface FormattedListAttributes extends HTMLAttributes {
        values?: FormattedList['values'],
        formatter?: FormattedList['formatter'],
        delim?: FormattedList['delim']
    }
  }

This would result in values: any[], etc.

It would not enforce type safety based on generics across attribute values, but the errors would go away.

@cjorasch
Copy link
Author

cjorasch commented Dec 5, 2017

The approach could be further simplified using Pick<>.

For the above example the components.d.ts file could contain:

export interface FormattedListAttributes extends
  HTMLAttributes,
  Pick<FormattedList, 'values' | 'formatter' | 'delim'> { }

This would restrict it to the named properties (vs. all members of the component class) and they would have the same data type as the component properties.

This could also be used for more accurate element types.

For example:

interface HTMLFormattedListElement extends
  Pick<FormattedList, 'values' | 'formatter' | 'delim'>,
  HTMLElement { }

Generics could also be supported:

interface HTMLFormattedListElement<T = object> extends
  Pick<FormattedList<T>, 'values' | 'formatter' | 'delim'>,
  HTMLElement { }

In this case the list of properties would include all members that are implemented on the element (including methods) but not members that only exist on the component class.

@jthoms1
Copy link
Contributor

jthoms1 commented Apr 3, 2018

The Type system generation has changed a lot. I am closing this issue because I believe this was fixed.

@jthoms1 jthoms1 closed this as completed Apr 3, 2018
@cjorasch
Copy link
Author

cjorasch commented Apr 3, 2018

Using JSX.Element as property type works now.

Using generic class for component does not work. For example, export class FormattedList<T = any> { ... }.

@jthoms1
Copy link
Contributor

jthoms1 commented Apr 4, 2018

@cjorasch I have been thinking about supporting this but how would you use generics in JSX Elements?

@cjorasch
Copy link
Author

cjorasch commented Apr 4, 2018

Currently the main value would be explicit typing of related properties, fields, parameters (see FormattedList example above). This allows for explicit assumptions when writing a component vs. having lots of any data types.

Type checking would be available when Typescript adds support for generic components in Typescript 2.9.

https://github.com/Microsoft/TypeScript/wiki/Roadmap
microsoft/TypeScript#22415

@jthoms1
Copy link
Contributor

jthoms1 commented Apr 4, 2018

Did not realize that was coming, Very cool! Since we are so closely typed to Typescript, it is likely that we will not allow for generics on Component Classes until 2.9 is released.

@davidenke
Copy link

@jthoms1 using generics for props is still not working in 0.16.1:

export class ExampleComponent<T extends string | number> {

  @Prop() value: T;

}

throws error:

[ ERROR ]  TypeScript: src/components.d.ts:2:13
           Cannot find name 'T'.
     L2: 
     L3:    'value': T;
     L4:

@davidenke
Copy link

It's not just the @Prop decorator, the @Event decorator fails as well.

@Event() change: EventEmitter<T>;

Only the generic declaration of the component has to be passed to the .d.ts.

@sameergoyal
Copy link

sameergoyal commented Aug 29, 2019

@jthoms1 This still doesnt work in Stencil & Typescript 2.9 with Generics for Component Classes has been released for over an year.

@ghost
Copy link

ghost commented Aug 29, 2019

Why is this issue closed? This is not fixed!

@vultix
Copy link

vultix commented Sep 24, 2019

@jthoms1 Any updates on this issue?

@zachariahtimothy
Copy link

So it is 2021 now...any guidance on getting around this?

@JustinSpedding
Copy link

I am also having the same issue that is listed in #350 (comment). Is there any workaround? If I manually edit the generated components.d.ts to add <T extends string | number> or whatever to the interface, it compiles. But this is not a solution because the file just get overwritten anyway.

@valentinbourqui
Copy link

Same issue in 2023 any workaround ?

@JustinSpedding
Copy link

JustinSpedding commented Sep 1, 2023

@valentinbourqui This PR was opened over a year ago which implements type parameters in class names: #3488

The ionic team has repeatedly put off even looking at the PR since it is "in the backlog," as can be read in the comments. It's not even a large amount of code. Maybe we'll get lucky and one day they will review it. Or not ¯\(ツ)

Honestly, I do not have high hopes for this framework going forward if they ignore such a crucial and requested feature that was already implemented for them. This, and several other shortcomings with the framework and the team, has led me to jump ship.

@valentinbourqui
Copy link

valentinbourqui commented Sep 1, 2023

@JustinSpedding unfortunately I must use Stencil but I'm so surprise to see that for an important but small change like that it takes years to be taken in consideration. That's so sad. I will follow the PR an cross finger for a merge. Thank you so much for your reply.

@DmitryAvanesov
Copy link

DmitryAvanesov commented Jun 5, 2024

Any updates on this issue?
I'm still unable to create generic components with stencil 4.18.3

@christian-bromann
Copy link
Member

A new issue for this was raised in #2895 - let's continue all conversations there. This is something the Stencil team is interested to implement.

@ionic-team ionic-team locked and limited conversation to collaborators Jun 5, 2024
This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests