vuejs / vetur

Vue tooling for VS Code.
https://vuejs.github.io/vetur/
MIT License
5.75k stars 593 forks source link

Narrowed type by v-if should not be widened in v-on etc. #1212

Open ktsn opened 5 years ago

ktsn commented 5 years ago

The current v-if narrowing implementation (#1208), there are some cases the narrowed type is widened to the original type.

<template>
  <div v-if="post">
    <!-- post is of type "Post" here -->

    <!-- post will be widened to "Post | null" in @click directive -->
    <button @click="onClick(post)">Click</button>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'

interface Post {
  id: string
}

export default Vue.extend({
  data() {
    return {
      post: null as Post | null
    }
  },

  methods: {
    onClick(post: Post) {
      // ...
    }
  }
})
</script>

This is because v-on emits callback code and TypeScript widens the narrowed type in a callback. We may need to assign some local variable to retain narrowed types.

kimamula commented 4 years ago

Is it possible to disable strict null checks of vti (or Vetur) only for v-on?

rchl commented 4 years ago

As far as I can tell, this is the correct behavior. The click handler is evaluated on click, not at the time of rendering the template. So the data variable post can be null at the time of click handler being executed.

yoyo930021 commented 3 years ago

Ref: https://github.com/vuejs/vetur/issues/2504#issuecomment-735761621

SuspiciousLookingOwl commented 3 years ago

Is this the same issue?

<template>
  <div>
    <div v-if="baz">
      <div v-for="x in [1, 2, 3, 4, 5]" :key="x">
        <!-- baz should be narrowed to `string`, but it's not -->
        {{ bar(baz) }}
      </div>
    </div>

    <div v-for="x in [1, 2, 3, 4, 5]" :key="x">
      <div v-if="baz">
        <!-- baz correctly narrowed to string if v-if is inside the loop -->
        {{ bar(baz) }}
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from "vue-property-decorator";

@Component
export default class AddToDialog extends Vue {
  get baz(): string | null {
    return "";
  }

  bar(x: string) {
    return x;
  }
}
</script>

image

dospunk commented 3 years ago

I am having the same issue that @SuspiciousLookingOwl is having, and it only seems to happen with "strict": true in the tsconfig. Without strict being on the narrowing performs as expected.

dospunk commented 3 years ago

I've made a reproduction of the bug with v-for here: https://github.com/dospunk/vetur-v-if-not-infering-bug

The error reported by Vetur on line 4 of test.vue should not be there

mattclough1 commented 1 year ago

As far as I can tell, this is the correct behavior. The click handler is evaluated on click, not at the time of rendering the template. So the data variable post can be null at the time of click handler being executed.

But is it possible for the click handler to be called if it can't be clicked due to the button not being rendered?