johnsoncodehk / vue-tsc

vue-tsc --noEmit && vite build
https://www.npmjs.com/package/vue-tsc
MIT License
241 stars 6 forks source link

Methods provided in setup() cause "cannot find name" error #4

Closed bartenra closed 3 years ago

bartenra commented 3 years ago

Does not error:

<template>
    <input @change="myChangeMethod" />
</template>

<script>
export default defineComponent({
  methods: {
    myChangeMethod(event) {
      return "hello";
    }
  }
});

Wrongly errors:

<template>
    <input @change="myChangeMethod" />
</template>

<script>
export default defineComponent({
  setup() {
    return {
      myChangeMethod(event) {
        return "hello";
      }
    }
  }
});
src/Test.vue:2:21 - error TS2304: Cannot find name 'myChangeMethod'.

2     <input @change="myChangeMethod" />
                      ~~~~~~~~~~~~~~

Found 1 error.
johnsoncodehk commented 3 years ago

Does Volar extension show this error?

bartenra commented 3 years ago

I was too fast to post. My minimal reproducible case actually doesn't give this error.

tschoartschi commented 3 years ago

@bartenra I have the exact same problem. How did you solve it?

bartenra commented 3 years ago

@tschoartschi Are you using Volar?

tschoartschi commented 3 years ago

@bartenra no currently I use Vetur. Since I need it for work and this project is only a side project I didn't want to crash my work environment with installing a new extension for Vue development.

But the good news is, I figured out the problem 🎉 I try to quickly explain what the problem was. Let's have a look at the following code:

  export default defineComponent({
    setup() {
      const handler = inject<Handler>('handler');
      const method1 = () => handler.runTask();
      return {
        method1,
      };
});

Now TypeScript complained that handler could be undefined. Then I added a null-check:

  export default defineComponent({
    setup() {
      const handler = inject<Handler>('handler');
      if(!handler) {
          console.error('no handler defined');
          return;
      }
      const method1 = () => handler.runTask();
      return {
        method1,
      };
});

Now TypeScript didn't complain while developing. But when I did the production build of course it caused problems because there is the possibility that the setup method returns undefined. Of course, this is easy to spot in this short example but the real code was more involved. So it took some time to find the problem. The error message was also not helpful because vue-tsc only said:

src/components/Example.vue:9:18 - error TS2304: Cannot find name 'method1'.

9         @change="method1"

Since in my case handler is always defined I did the following:

      const handler = inject<Handler>('handler')!; // add "!" at the end to tell TypeScript that handler is always defined

Now everyone is happy and the build works again 🎉

bartenra commented 3 years ago

@tschoartschi I think that is a bit of an escape hatch. After all, it might not be defined if you forgot to provide it in a parent component.

You might find this helpful: https://logaretm.com/blog/2020-12-23-type-safe-provide-inject/

tschoartschi commented 3 years ago

@bartenra thanks for the link 🙂 it's a very interesting read and especially injectStrict is a nice solution for our problem. In our case, the object is really always defined because it's a singleton that is available in the whole application. But this injectStrict will make the code definitely better.