Closed jfparadis-appomni closed 1 year ago
It was added for new users to understand these two points.
$event
variable in Inline handlersv-if
type narrowing does not workHowever, depending on user experience this may also be a nuisance, so just disable it in user space settings as needed.
Thanks, John; your explanation is much appreciated.
You're welcome, I'm Johnson btw. :)
For those who need more context:
Volar now proposes to add $event =>
to event handlers via Intellisense:
You can disable this feature in Volar’s settings or with volar.inlayHints.eventArgumentInInlineHandlers
.
Johnson mentions two reasons for the addition:
$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-handlersBoth 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=
v-if
. See https://github.com/vuejs/language-tools/issues/1249 and https://github.com/vuejs/language-tools/issues/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
Thanks! Just link your explanation to the hint.
Volar 1.1.4 added a new setting,
volar.inlayHints.eventArgumentInInlineHandlers
, enabled by default. Here's the commit: https://github.com/vuejs/language-tools/commit/274b027cf30482b95fd20ff822b66b40698cec87The commit doesn't reference an issue; we're very interested to know more about the intent and how to leverage it best!