Closed7

Vue 2 + Composition API の型チェックについて調べる

Yuku KotaniYuku Kotani

なにか怪しいところがあったら教えてください

前提

  • Nuxt 環境だけど Vue 2 一般に当てはまると思う
  • Vue 3 にもそれなりに当てはまりそう
  • もちろんエディタでも CI でもチェックしたい
Yuku KotaniYuku Kotani

props の型は PropType で定義できる。

props: {
  book: {
    type: Object as PropType<Book>,
    required: true,
  }
},
setup(props) {
  // props.book is typed as Book
}

ただし、import が必要な型で PropType を使うと Vetur がコンポーネントの呼び出し側で cannot find name エラーを吐いてしまう問題がある(vuejs/vetur#2457)

そのため、import が必要な型を含む場合は、今のところ PropType を使わないワークアラウンドを用いたほうがよさそう。

props: {
  book: {
    type: Object as () => Book,
    required: true,
  }
},
setup(props) {
  // props.book is typed as Book
}

Function props の引数や返り値の型に import が必要なときもやはり PropType は使えない。この場合、Function をそのままキャストできないので、以下のように unknown を噛ませることになる。

Before:

props: {
  callback: {
    type: PropType<(book: Book) => void>,
    required: true,
  }
},
setup(props) {
  // props.callback is typed as `(book: Book) => void`
}

After:

props: {
  callback: {
    type: (Function as unknown) as () => (book: Book) => void,
    required: true,
  }
},
setup(props) {
  // props.callback is typed as `(book: Book) => void`
}
Yuku KotaniYuku Kotani

呼び出し側の props 型チェックをしっかり行う方法は今のところなさそう。

Vetur を使うと以下のことはできる。

  • required な props を渡しているかチェック
  • string や number などのプリミティブな props の型チェック

PropType で定義した型への対応の議論は vuejs/vetur#2344 で行われている。

Yuku KotaniYuku Kotani

Vetur Terminal Interface という実験的なプロジェクトがあり、これを使うと Vetur でできるチェックを CI で行うことができる。required props を strict にできるだけでも結構助かる。

ただし結構時間がかかって、手元の eslint check が 25s で終わるプロジェクトだとだいたい 300s かかった。

Vetur に型チェックを実装してくれた ktsn さんの記事によるとかなりの腕力で実装されているみたいで(すごい)、仕方ない部分ではあると思う。

Yuku KotaniYuku Kotani

WebStorm 使いに聞いたところ、props の検査は今のところできないらしい。僕は使ってないからわからないけど。

Yuku KotaniYuku Kotani

テンプレート内での変数型チェックは props の型情報と setup 関数の戻り値の型情報をいい感じに合成しているっぽく、戻り値を返さない setup 関数を定義すると props の型まで壊れて never になった。setup 関数の戻り値がない場合は空オブジェクトを返すか、そもそも setup 関数が不要な場合は定義しないほうがよい。

例えば以下のように定義したコンポーネントで、テンプレートから book を読むと never 型になる。

props: {
  book: {
    type: Object as () => Book,
    required: true,
  }
},
setup() {}
このスクラップは2021/12/09にクローズされました