Skip to content

Template interpolation incorrect type on event (Typescript) #2504

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

Closed
4 tasks done
SuspiciousLookingOwl opened this issue Nov 29, 2020 · 10 comments
Closed
4 tasks done

Comments

@SuspiciousLookingOwl
Copy link

  • I have searched through existing issues
  • I have read through docs
  • I have read FAQ
  • I have tried restarting VS Code or running Vetur: Restart VLS

Info

  • Platform: Windows
  • Vetur version: 0.30.3
  • VS Code version: 1.51.1

Problem

Vetur is reporting false error with "vetur.experimental.templateInterpolationService": true due to inccorect typing when passing a variable from an event, example:

  <!-- foo is computed property with return type number | undefined -->

  <div v-if="foo">
    <!-- foo now should be number  -->
    {{ foo }} <!-- Correct type (number) -->
    <input :example="foo" /> <!-- Also correct type (number) -->
    <input type="text" @input="bar(foo)" /> <!-- Incorrect type (number | undefined) -->
  </div>

image

Reproducible Case

<template>
  <div v-if="foo">
    <!-- foo now should only be number  -->
    {{ foo }} <!-- Correct type (number) -->
    <input :example="foo" /> <!-- Also correct type (number) -->
    <input type="text" @input="bar(foo)" /> <!-- Incorrect type (number|undefined) -->
  </div>
</template>

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

@Component
export default class Test extends Vue {
  get foo(): number | undefined {
    return 1;
  }

  bar(x: number) {
    return x * 2;
  }
}
</script>
@rchl
Copy link
Collaborator

rchl commented Nov 30, 2020

This is the correct behavior.

The handler of the input event will be evaluated on entering the text in the input. That can happen at arbitrary times. So the value can be undefined at that point.

@rchl rchl added the question label Nov 30, 2020
@SuspiciousLookingOwl
Copy link
Author

So the @input can be triggered even though it's not rendered because of the v-if="foo"?

@yoyo930021
Copy link
Member

@rchl
Copy link
Collaborator

rchl commented Nov 30, 2020

So the @input can be triggered even though it's not rendered because of the v-if="foo"?

Good point I guess. It should theoretically not be possible to trigger the event handler if foo is undefined. But this relation might be too complex for a tool like Vetur (or Typescript) to understand.

@SuspiciousLookingOwl
Copy link
Author

So the @input can be triggered even though it's not rendered because of the v-if="foo"?

https://www.typescriptlang.org/play?#code/DYUwLgBAZg9jBcEB2BXAtgIxAJwgH2RWGAgF4IBGAKCoGMYkBnSetABzIgAoBKMgPggBvKhAgBLKN1gw+IsWJkA6AG4BDYChAB5KLxoKI9Ji2DjaAa068B0OKo1bd+sQF8q7qjM6piQA

That's true for Typescript, but does Vue work the same?

Since I put <div v-if="foo"> so technically foo inside that div is not undefined, therefore the foo in @input="bar(foo)" is supposed to be not undefined. Unless it's possible for Vue to trigger the @input even though the component is not rendered because of the <div v-if="foo"> when the foo is undefined, which seems to be what you are trying to say.

@yoyo930021
Copy link
Member

@ktsn what do you think about this?

@ktsn
Copy link
Member

ktsn commented Nov 30, 2020

#1212

I've been thinking of this problem and there seem to be two approaches:

  1. Change template transformer implementation to like what Angular does (use if statement and user defined type guard to generate context, in this case $event, instead of using helper function and callback) so that we can eliminate callbacks from transformed code.

https://medium.com/angular-in-depth/type-checking-templates-in-angular-viewengine-and-ivy-77f8536359f5

  1. Assign all values in if condition into local variables so that we avoid type widening.
if (vm.foo.bar.baz) {
  const __foo = vm.foo
  const __foo_bar = vm.foo.bar
  const __foo_bar_baz = vm.foo.bar.baz

  {
    on: {
      input: $event => func(__foo_bar_baz)
    }
  }
}

The latter approach should be easier than the former but I'm not sure if it actually works properly. There might be some edge cases that I'm not aware of.

@yoyo930021
Copy link
Member

#1212

I've been thinking of this problem and there seem to be two approaches:

  1. Change template transformer implementation to like what Angular does (use if statement and user defined type guard to generate context, in this case $event, instead of using helper function and callback) so that we can eliminate callbacks from transformed code.

https://medium.com/angular-in-depth/type-checking-templates-in-angular-viewengine-and-ivy-77f8536359f5

  1. Assign all values in if condition into local variables so that we avoid type widening.
if (vm.foo.bar.baz) {
  const __foo = vm.foo
  const __foo_bar = vm.foo.bar
  const __foo_bar_baz = vm.foo.bar.baz

  {
    on: {
      input: $event => func(__foo_bar_baz)
    }
  }
}

The latter approach should be easier than the former but I'm not sure if it actually works properly. There might be some edge cases that I'm not aware of.

I think we can choose the latter one.
Just adjust as necessary.

@yoyo930021
Copy link
Member

I will close this issue.
Please follow #1212

@yoyo930021
Copy link
Member

Duplicate of #1212

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

4 participants