🦮
Vueのemit('modelValue', value)で型安全性が崩壊!?気をつけて!
結
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
ちょうど本日発表のVue3.3で(まだexperimentalですが)
defineModel
が来ましたので、こちらを使えば簡潔にかつ型安全にv-modelを扱えそうですね! (リリース記事こちら)🥳🎉