Open9

vue3 で emit に型をつける方法を調べる

Ryosuke MiyamotoRyosuke Miyamoto

前提として例えば、下記のような親子コンポーネントがあった場合、親の doSomething 関数の引数 count が number 型 として返ってきて欲しい。

Parent.vue
<template>
<div>
  <Child @updateCount="doSomething" />
</div>
<template>

<script lang="ts">
import { defineComponent, ref } from 'vue'

export default defineComponent({
  components: {
   Child: ()=> import('./Child.vue')
  },
  setup(_, { emit }) {
    return {
      const text = ref<string>('MyText')
      doSomething: (count)=> {
        text.value = count // 怒られてほしい        
      }
    }
  }
})
</script>
Child.vue
<template>
  <button @click="incrementCount" />
<template>

<script lang="ts">
import { defineComponent, ref } from 'vue'

export default defineComponent({
  setup(_, { emit }) {
    const count = ref<number>(0)
    return {
      incrementCount: ()=> {
        count.value++
        emit('updateCount', count.value)
      }
    }
  }
})
</script>

※ 親コンポーネントで count を管理したほうがいいのでは?など設計の話は一旦無視する

Ryosuke MiyamotoRyosuke Miyamoto

これで効くのようになると思ったけど効かぬ。

Child.vue
<script lang="ts">
import { defineComponent, ref } from 'vue'

export default defineComponent({
  emits: {
    updateCount: (count: number) => {
      return typeof count === 'number'
    }
  },
  setup(_, { emit }) {
    const count = ref<number>(0)
    return {
      incrementCount: ()=> {
        count.value++
        emit('updateCount', count.value)
      }
    }
  }
})
</script>
Ryosuke MiyamotoRyosuke Miyamoto

このruntime error だったので vetur オフにしないといけないのかな?と思ったけど、vetur オフにしても補完効かず、ts のほうのエラーは残る。

Parent.vue
      doSomething: (count)=> {
        text.value = count // 怒られてほしい        
      }

Parameter 'count' implicitly has an 'any' type.Vetur(7006)
Parameter 'count' implicitly has an 'any' type.ts(7006)

もちろん number つければエラーなくなるが意味ない。

Parent.vue
      doSomething: (count: number)=> {
        text.value = count       
      }
Ryosuke MiyamotoRyosuke Miyamoto

上記サイト、親コンポーネントについては書いてないので、emit 発火だけの話?

        const onClick = () => {
            emit("update", 100)
            // emit("hoge") これはエラーになる
            // emit("update", "a") 引数の型が一致しないのでこれもエラーになる
        }
Ryosuke MiyamotoRyosuke Miyamoto

いや、子コンポーネントで上記のエラーも出ないな。何か初歩的ミスしてそう

Ryosuke MiyamotoRyosuke Miyamoto

公式 の発見したので、ペタ。

const Component = defineComponent({
  emits: {
    addBook(payload: { bookName: string }) {
      // ランタイムバリデーションの実行
      return payload.bookName.length > 0
    }
  },
  methods: {
    onSubmit() {
      this.$emit('addBook', {
        bookName: 123 // 型エラー!
      })

      this.$emit('non-declared-event') // 型エラー!
    }
  }
})

https://v3.ja.vuejs.org/guide/typescript-support.html#emit-にアノテーションをつける

ん?そもそも setup 関数じゃないけど効くの? vscode の問題な気がしてきた