🦮

Vueのemit('modelValue', value)で型安全性が崩壊!?気をつけて!

2023/05/11に公開2

Vue.jsのemit('update:modelValue', value)modelValueと疎結合なので、以下のような用法によって、型健全性が壊れます!!

const props = defineProps<{
  modelValue: T
}>()

const emit = defineEmits<{
  (e: 'update:modelValue', value: U): void  // 間違ってvalueがTでなくUになっている
}>()

const fun = () => emit('update:modelValue', u)  // u: U

解説

解説する必要もないくらい単純なものですが、一応解説させていただきます :)

e: 'update:modelValue'を持つemitはVue.jsで特別扱いされています。
例えば以下のFoo.vueのbuttonをclickすると、emit('update:modelValue', 42)により、props.modelValueが42にセットされ、それによりindex.vueのfooも同様に42にセットされます。

Foo.vue
<template>
  <button @click="fun">42</button>
</template>

<script setup lang="ts">

</script>
const props = defineProps<{
  modelValue: number
}>()

const emit = defineEmits<{
  (e: 'update:modelValue', value: number): void
}>()

const fun = () => emit('update:modelValue', 42)
index.vue
<template>
  <div>
    <Foo v-model="foo">
    foo: {{ foo }}
  </div>
</template>

<script setup lang="ts">
const foo = ref(0)
</script>

ここで

Foo.vue
const emit = defineEmits<{
  (e: 'update:modelValue', value: number): void
}>()

const fun = () => emit('update:modelValue', 42)

Foo.vue
const emit = defineEmits<{
  (e: 'update:modelValue', value: Error): void
}>()

const fun = () => emit('update:modelValue', new Error('msg'))

に間違えてみましょう。
Foo.vueのmodelValueはnumberで

Foo.vue
const props = defineProps<{
  modelValue: number
}>()

同様にindex.vueのfooもRef<number>なので

index.vue
const foo = ref(0)

明らかにErrorになるのはおかしいです。

しかし実際はFoo.vueのbuttonをclickすると、fooの値がnew Error('msg')になります。
つまりfooがRef<number>なのにRef<Error>の値が入っており、これは型健全性を損なっています。

ここには注意しましょう。
型が矛盾します。

const emit = defineEmits<{
  (e: 'update:modelValue', value: ここ): void
}>()

Discussion