vetur icon indicating copy to clipboard operation
vetur copied to clipboard

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

Open ktsn opened this issue 6 years ago • 7 comments

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.

ktsn avatar Apr 19 '19 03:04 ktsn

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

kimamula avatar Jul 25 '20 06:07 kimamula

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.

rchl avatar Jul 25 '20 12:07 rchl

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

yoyo930021 avatar Nov 30 '20 13:11 yoyo930021

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

SuspiciousLookingOwl avatar Dec 02 '20 11:12 SuspiciousLookingOwl

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 avatar Jul 09 '21 21:07 dospunk

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

dospunk avatar Jul 09 '21 21:07 dospunk

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?

mattclough1 avatar Feb 09 '23 20:02 mattclough1