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

Question about "visualize event argument" #2445

Closed
jfparadis-appomni opened this issue Feb 22, 2023 · 5 comments
Closed

Question about "visualize event argument" #2445

jfparadis-appomni opened this issue Feb 22, 2023 · 5 comments
Labels
question Further information is requested

Comments

@jfparadis-appomni
Copy link
Contributor

Volar 1.1.4 added a new setting, volar.inlayHints.eventArgumentInInlineHandlers, enabled by default. Here's the commit:
274b027

The commit doesn't reference an issue; we're very interested to know more about the intent and how to leverage it best!

@johnsoncodehk
Copy link
Member

It was added for new users to understand these two points.

  • The presence of the $event variable in Inline handlers
  • Inline handlers are actually a callback, so it is correct that v-if type narrowing does not work

However, depending on user experience this may also be a nuisance, so just disable it in user space settings as needed.

@johnsoncodehk johnsoncodehk added the question Further information is requested label Feb 22, 2023
@jfparadis-appomni
Copy link
Contributor Author

Thanks, John; your explanation is much appreciated.

@johnsoncodehk
Copy link
Member

You're welcome, I'm Johnson btw. :)

@jfparadis-appomni
Copy link
Contributor Author

For those who need more context:

Volar now proposes to add $event => to event handlers via Intellisense:
Screenshot 2023-02-24 at 3 56 20 PM

You can disable this feature in Volar’s settings or with volar.inlayHints.eventArgumentInInlineHandlers.

Johnson mentions two reasons for the addition:

  1. Vue already exposes the variable $event in event handlers. Volar proposes adding an arrow function to make it more obvious. See https://vuejs.org/guide/essentials/event-handling.html#accessing-event-argument-in-inline-handlers

Both styles below are correct, but the first one might surprise a new user because $event doesn't exist on the component instance, only in event callbacks:

  <button @click="warn('Form cannot be submitted yet.', $event)">
    Submit
  </button>

  <button @click="$event => warn('Form cannot be submitted yet.', $event)">
    Submit
  </button>

Note that Vue compiles both styles to the same result, so you’re not losing any performance by adding the $event => wrapper:

    _createElementVNode("button", {
      onClick: _cache[0] || (_cache[0] = $event => (_ctx.warn('Form cannot be submitted yet.', $event)))
    }, " Submit "),
    _createElementVNode("button", {
      onClick: _cache[1] || (_cache[1] = $event => _ctx.warn('Form cannot be submitted yet.', $event))
    }, " Submit ")

Check for yourself in this playground (click on the JS tab to see how the template is compiled): https://sfc.vuejs.org/#eNqtkD0OwjAMha9iWUiABMleFQQLF2DN0hYDBeJEiUuFqt6dpkVMjGz+ef6s9[…]SeoNXER8zreO5SjndonLhoodKhYaltqQo2nUZXBspDGCDCdEb7rF/A3K8dfo=

  1. Event handlers are callbacks, and types won’t be narrowed inside event handlers by a v-if. See [Feature Request] infer v-if type narrowing on the @event #1249 and Types don't seem to be narrowed inside of event handlers #269 and many, many other discussions on the subject.

In other words, assume that foo is a data property of type object or undefined:

  <div v-if="foo">

    <!-- 'foo' is narrowed correctly to an object inside of the curly braces -->
    Test 1: {{ foo.bar }}

    <!-- 'foo' is narrowed correctly to an object inside of the assignment to the property ':title' -->
    <span :title="foo.bar">Test 2 nested</span>

    <!-- 'foo' still possibly undefined inside of `@click` and you get a TS error "foo is possibly undefined" -->
    <button @click="alert(foo.bar)">
      Submit
    </button>

    <!-- same error, and this wrapper makes it more obvious -->
    <button @click="$event => alert(foo.bar)">
      Submit
    </button>
  </div>

Why is this happening, and why the wrapper makes it more obvious?

The v-if and the @click handler execute at different times, with foo taking different values. In other words, the type narrowing provided by the v-if is lost by the time the callback is involved. Adding an arrow function makes it clearer that a callback is present.

Many people stumble on this one, which has nothing to do with Volar or Vue. This can be reproduced in pure TypeScript:

let foo: string | undefined;
if (foo) {
  ($event: any) => onSelect(foo) // Argument of type 'string | undefined' is not assignable to parameter of type 'string'.ts(2345)
}
function onSelect(foo: string) { }

Check for yourself in this playground:
https://www.typescriptlang.org/play?#code/DYUwLgBAZg9jBcEDOYBOBLAdgcwgHwgFdMATEKLEEgbgCh0oI[…]GxnADowJCYAJgBmABYAVjZaAF9aKGItdGUlVXUQLR0EZCKcDk4IFqA

@johnsoncodehk
Copy link
Member

Thanks! Just link your explanation to the hint.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants