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

Using props in tsx is not possible #2417

Closed
DCzajkowski opened this issue Aug 31, 2018 · 22 comments
Closed

Using props in tsx is not possible #2417

DCzajkowski opened this issue Aug 31, 2018 · 22 comments

Comments

@DCzajkowski
Copy link

Version

3.0.1

Reproduction link

https://github.com/DCzajkowski/vue-jsx-reproduction

Node and OS info

Node v9.11.2 / yarn 1.9.4 / macOS

Steps to reproduce

$ yarn && yarn serve

What is expected?

Compilation successful.

What is actually happening?

An error appears

ERROR in /Users/Darek/web/sites/test2-vue-project/src/views/Home.tsx
9:21 Property 'msg' does not exist on type 'ComponentOptions<Vue, DefaultData<Vue>, DefaultMethods<Vue>, DefaultComputed, PropsDefinition<Record<string, any>>, Record<string, any>> | ThisTypedComponentOptionsWithArrayProps<Vue, object, object, object, never> | ThisTypedComponentOptionsWithRecordProps<...> | undefined'.
     7 |     return (
     8 |       <div class='home'>
  >  9 |         <HelloWorld msg='Welcome to Your Vue.js   TypeScript App' />
       |                     ^
    10 |       </div>
    11 |     )
    12 |   }
No lint errors found
Version: typescript 3.0.3, tslint 5.11.0
Time: 9742ms

I am very sorry if this is a set-up problem on my side or if it's not a correct repo to report this kind of stuff. I don't have experience with TSX and really can't find a solution to why this may not work. Sorry if it's a duplicate.

P.S. is vuejs/vue#6856 related?

@Akryum Akryum added scope: typescript needs team repro We acknowledged your report and will soon try to reproduce it labels Sep 2, 2018
@Leandro-Albano
Copy link

Leandro-Albano commented Sep 8, 2018

I got the same here.

Just created a component

import { Component, Vue, Prop } from 'vue-property-decorator';

@Component()
export default class NavLink extends Vue {

    @Prop()
    public widget!: string;

    @Prop({ default: '#' })
    public href!: string;

    @Prop()
    public toggle!: string;

    protected render(h: any) {
        return (
                <a class='nav-link' data-widget={this.widget} href={this.href} data-toggle={this.toggle}>
                    {this.$slots.default}
                </a>
        );
    }
}

And tried to use it in another one

import { Component, Vue } from 'vue-property-decorator';
import NavLink from '@/components/core/main-header/nav-link/nav-link';

@Component({
    components: {
        'nav-link': NavLink
    },
    name: 'main-header'
})
export default class MainHeader extends Vue {

    public render(h: any) {
        return (
            <NavLink widget='pushmenu'>
                <i class='fa fa-bars'></i>
            </NavLink>
        );
    }
}

Than I got the error

Property 'widget' does not exist on type 'ThisTypedComponentOptionsWithArrayProps<Vue, object, object, object, never> | ThisTypedComponentOptionsWithRecordProps<Vue, object, object, object, object> | ComponentOptions<...> | undefined'.

If instead of the <NavLink widget='pushmenu'> I use <nav-link widget='pushmenu'> it works fine. Or, if the attribute was something-widget instead of just widget, it works fine too.

@manneredboor
Copy link

@Leandro-Albano your solution does not provide valid type checking for props. Bump the topic

@Peccansy
Copy link

same problem for me

@nandin-borjigin
Copy link

@Akryum OP has already provided a reproduction link.
If it's out-of-date or so, I just made another one: vue-tsx

you can reproduce by npm install npm run serve and then inpsect the typescript type-check error (the code compiles and works as expected, but the tsc emits an error and so does the VSCode)
image

@brunolau
Copy link

brunolau commented Nov 7, 2018

Same problem for me...is there any workaround that would keep the type-checking?

@ericettensohn
Copy link

Same here

@DCzajkowski
Copy link
Author

@Akryum @yyx990803 Can we please get any update on that?

@DCzajkowski
Copy link
Author

I have just cloned my repo, ran a fresh yarn and what I've noticed is that:

  • it works in the browser
  • error is thrown in the console, but it doesn't seem to mean anything (the in-browser code works)
  • the code is continuously being rebuilt, even though there are no changes
  • changing private to public does not affect the above behaviour

@brunolau
Copy link

brunolau commented Nov 8, 2018

I've managed to resolve the problem for my scenario using the following pattern
[sorry if the guide is a bit longer and not suitable for github, but might help some people having the same problem]

First of all I've extended the JSX.ElementAttributesProperty interface and created an abstract Vue extension class to serve as a base for the component. So let's create a ts file [e.g. vue-tsx.ts] somewhere in your project and place the following in there

import Vue from "vue";

export default abstract class TsxComponent<P> extends Vue {
    private vueTsxProps: Readonly<{}> & Readonly<P>;
}

declare global {
    namespace JSX {
        interface ElementAttributesProperty { vueTsxProps: {}; }
    }
}

Now the component has to be created using following pattern - It's essential that there's an interface defined and implemented by the component - ensures the type-safety. This is passed to the TsxComponent base class as a generic argument which creates the dummy vueTsxProps property (see the abstract class above), which is served to the TS typechecker as the valid parameter list. So let's create a sample component like this

import TsxComponent from "../../vuetsx"; //this is the relative path to the file created above
import { Component, Prop } from 'vue-property-decorator'

interface PersonDataArgs {
    username: string;
    age: number
}

@Component
export default class MyComponent extends TsxComponent<PersonDataArgs> implements PersonDataArgs {
    @Prop() username!: string;
    @Prop() age!: number;

    render() {
        return <span>Hello {this.username}, you are supposed to be {this.age} old</span>
    }
}

Now when using the component, correct type-checking is applied [1st one denied due to missing mandatory property, 2nd one ok, 3rd one denied due to unknown property applied]

image

@DCzajkowski
Copy link
Author

@brunolau It's a nice hack, but it forces you to write types and prop declarations twice, which doesn't seem like a good idea :/

@brunolau
Copy link

brunolau commented Nov 8, 2018

Yes, that's the downside of the approach. However you are still protected by the compiler type checking. As you are both passing the interface type to the base class as well as implementing it on the component class, you are getting protected from making a typo on either side

Let's figure you want to add property "birthYear: number" to the component. If you forget to add it to the interface, you will get compile error from the TSX template and if you forget to add it to the component class, you will get compile error stating that the component class doesn't implement the interface.

Still, I fully agree that having to write the property twice isn't the ideal approach, however I wasn't able to find any other way besides not using the property decorators. [If you want to stick with props: {} annotation rather than property decorators, just change the "vueTsxProps" to "props" from my example and you should be good. There are also some other ways for property decorators, these will however make all the component properties optional as well as include all Vue base class properties in the intellisense, for more info see This issue on Typescript repo

As I wanted to stick with property decorators, I've decided to gowith the approach described above and I'm affraid that until the Vue version 3 is out, one will always have to choose the "smaller evil"

@Leandro-Albano
Copy link

Leandro-Albano commented Nov 22, 2018

@manneredboor ,

That was not a solution, I'm just giving as much info as I have about the issue. Maybe I should say that it compiles fine, not that it works fine.

@lmiller1990
Copy link
Member

lmiller1990 commented Dec 6, 2018

I have been using this library to great success, you can get typechecking for both props and events.
https://github.com/wonderful-panda/vue-tsx-support

There is also a vue-cli-plugin to configure it for you.

import * as tsx from 'vue-tsx-support'

const Foo = tsx.component({
  name: 'Foo',

  props: {
    msg: {
      type: String,
      required: true as true
    }
  },

  render() {
    return (
      <span>Hi</span>
    )
  }
})

const Test = {
  name: 'Test',

  components: {
    Foo
  },

  render() {
    return (
      <div>
        <Foo  /> // ERROR since you did not provide the props
      </div>
    )
  }
}

@nextprops
Copy link

"vue": "^2.5.17",
"typescript": "^3.0.0",
"vue-class-component": "^6.0.0",

Me too~~
Property 'data' is only alternative,Hope it can be solved.

wx20181210-234936 2x

@LinusBorg LinusBorg added enhancement and removed needs team repro We acknowledged your report and will soon try to reproduce it labels Dec 27, 2018
@LinusBorg
Copy link
Member

I'm not sure that we should solve this within vue-cli.

The state of TSX and Vue 2.* simply is what we see here: without using a lib like vue-tsx-support, TSX won't work reliably. (sidenote: TSX should work out of the box with Vue 3.)

Seeing as that lib also has a cli-plugin already, I'm not sure we can do much in CLI core to further improve the sitution.

CommentS?

@DCzajkowski
Copy link
Author

Thank you for this answer. I was not aware of vue-tsx-support nor its cli. I'll investigate. Thanks!

@lmiller1990
Copy link
Member

I agree with @LinusBorg ... until Vue 3 hits, TSX can be supported by a third party lib. Although I think when Vue 3 does come out, TS support should be a first class citizen (including TSX) in Vue CLI.

@DCzajkowski vue-tsx-support does not have a cli itself, but if you are using vue cli, you can run vue add vue-tsx-support and it should configure it for you. You need to have selected the babel option when you set up your app using CLI (or add it in, however you do that), or you may have problems.

@LinusBorg
Copy link
Member

I'll close this then. Nothing further to do.

@esai256
Copy link

esai256 commented Jan 8, 2019

you can run vue add vue-tsx-support and it should configure it for you.

It's vue add tsx-support, just in case someone wonders, why they get an 404 :)

@rajibulnsu
Copy link

rajibulnsu commented Sep 14, 2020

@Akryum OP has already provided a reproduction link.
If it's out-of-date or so, I just made another one: vue-tsx

you can reproduce by npm install npm run serve and then inpsect the typescript type-check error (the code compiles and works as expected, but the tsc emits an error and so does the VSCode)
image

For me the work around was by updating the shims-tsx.d.ts file

import Vue, { VNode } from 'vue';

declare global {
  namespace JSX {
    // tslint:disable no-empty-interface
    interface Element extends VNode {}
    // tslint:disable no-empty-interface
    interface ElementClass extends Vue {}
    interface IntrinsicElements {
      [elem: string]: any;
    }

    // ** Add the following lines to solve the issue **
    interface ElementAttributesProperty{
      $props: {}
    }
  }
}

I know its after a very long time. Just incase someone also fetches the same issue.

@duykieu
Copy link

duykieu commented Dec 17, 2020

It makes me crazy to set up a project with typescript and jsx. From now, I should use React if wanna work with jsx. Too many problems

@miyukoarc
Copy link

I got the same here.

Just created a component

import { Component, Vue, Prop } from 'vue-property-decorator';

@Component()
export default class NavLink extends Vue {

    @Prop()
    public widget!: string;

    @Prop({ default: '#' })
    public href!: string;

    @Prop()
    public toggle!: string;

    protected render(h: any) {
        return (
                <a class='nav-link' data-widget={this.widget} href={this.href} data-toggle={this.toggle}>
                    {this.$slots.default}
                </a>
        );
    }
}

And tried to use it in another one

import { Component, Vue } from 'vue-property-decorator';
import NavLink from '@/components/core/main-header/nav-link/nav-link';

@Component({
    components: {
        'nav-link': NavLink
    },
    name: 'main-header'
})
export default class MainHeader extends Vue {

    public render(h: any) {
        return (
            <NavLink widget='pushmenu'>
                <i class='fa fa-bars'></i>
            </NavLink>
        );
    }
}

Than I got the error

Property 'widget' does not exist on type 'ThisTypedComponentOptionsWithArrayProps<Vue, object, object, object, never> | ThisTypedComponentOptionsWithRecordProps<Vue, object, object, object, object> | ComponentOptions<...> | undefined'.

If instead of the <NavLink widget='pushmenu'> I use <nav-link widget='pushmenu'> it works fine. Or, if the attribute was something-widget instead of just widget, it works fine too.

I donot know why, but it works.😂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests