Subscribe on changes!

Known events aren't removed from $attrs

avatar
Nov 12th 2020

Version

3.0.2

Reproduction link

https://jsfiddle.net/pqte0h2s/

Steps to reproduce

Just like declared $props are removed from $attrs and are never inherited, declared $emits should also be removed from $attrs.

It looks like they aren't and this can cause unexpected behaviors. In the linked repro, try to type in the input box. The outer y variable shouldn't change.

What is expected?

y shouldn't change, because the C1 component doesn't raise update:modelValue event.

onUpdateModelValue can be found in $attrs, which I wouldn't expect.

What is actually happening?

y is updated, because the app @update:modelValue event was put into $attrs, which the C1 component passes down to input.

Only unknown events (that are not declared in emits) should be found in $attrs.

avatar
Nov 12th 2020

BTW, if you pass null or undefined to an event handler, Vue still binds it and crashes when trying to call it. So the following isn't a convenient way to work-around this issue:

<input v-bind="{ ...$attrs, "onUpdate:modelValue": undefined }">

In some places, such as functional components, it is convenient to be able to pass an optional event handler without resorting to if statements. I leave it to your judgement if that's something you think is worth supporting.

avatar
Nov 12th 2020

I think you have a typo in your test case. It should be emits, not emit.

avatar
Nov 12th 2020

@skirtles-code Thank you and that seems to fix the repro! I'll check tomorrow in my real app if that was the issue after all.

Although if that is, I'm surprised everything was ok with TS and there was nothing on the console in dev mode. Are you not supposed to get some kind of warnings if you raise undeclared events?

avatar
Nov 13th 2020

Follow-up on that issue from my real project: It was indeed just a typo in emits that I did not notice.

TS was not kicking in because if you don't declare any emits at all, then emit() is typed to accept any string, so it worked. I believe there is no runtime warning for the same reason.

eslint-vue-plugin has a rule for that and it should have warned me. And that's actually an SFC tooling bug where some warnings seem to be lost in incremental rebuilds. I opened vuejs/vue-loader#1760 to track this.