LoginSignup
0

Vue.jsコンポーネントのpropsにHTMLElementを型付する

Last updated at Posted at 2023-08-30

Vue.jsのコンポーネントのpropsにHTMLElementを型付しようとしてハマまりました。
やろうとしたことは

props: {
  element: {type: Object as PropType<HTMLElement>, required: true}
}

と記述するとTypescriptではエラーが出ないが、Vueのランタイムエラーで

[Vue warn]: Invalid prop: type check failed for prop "element". Expected Object, got HTMLDivElement

となるという現象でした。(※親コンポーネントでHTMLDivElementを取得する処理を書いてます。)

動作環境は以下の通りです。

  • Vue: 2.7.14
  • Typescript: 4.6.2

解決方法

Vue.jsコンポーネントのpropsにHTMLElementを型付する方法です。
PropTypeを使用せず、直接HTMLELementを指定することでランタイムエラーも回避することができました。

props: {
  element: {type: HTMLElement, required: true}
}

ランタイムエラーになるのはなぜか

vuejs/vue at v2.7.14のソースコードを読んで原因を探ります。

こちらのエラーメッセージを生成している箇所を探すと

[Vue warn]: Invalid prop: type check failed for prop "element". Expected Object, got HTMLDivElement

vue/src/core/util/props.ts#L215-L217 at v2.7.14 · vuejs/vue
スクリーンショット 2023-08-31 10.45.35.png

function getInvalidTypeMessage の呼び出し元はこちらでした。

vue/src/core/util/props.ts#L135 at v2.7.14 · vuejs/vue
スクリーンショット 2023-08-31 10.47.15.png

呼び出し元のfunction名を見てみると function assertProp なので、prop関連についての処理を行ってそうです。

vue/src/core/util/props.ts#L105-L111 at v2.7.14 · vuejs/vue
スクリーンショット 2023-08-31 10.52.21.png

そのまま読み進めてみると、assertType を呼び出して型チェックをしていそうな箇所に行き着きました。
vue/src/core/util/props.ts#L126-L130 at v2.7.14 · vuejs/vue
スクリーンショット 2023-08-31 10.48.36.png

function assertType を読んでみると、期待する型がObjectの場合、isPlainObject を呼び出して実際に渡された値がObjectかどうかの確認をしていそうです。
vue/src/core/util/props.ts#L168-L169 at v2.7.14 · vuejs/vue
スクリーンショット 2023-08-31 10.54.34.png

function isPlainObject を見てみます。
vue/src/shared/util.ts#L58-L64 at main · vuejs/vue
スクリーンショット 2023-08-31 10.56.56.png

_toString.call(obj) の部分を見ていきます。

callメソッドについて調べてみると

call() はあるオブジェクトに所属する関数やメソッドを、別なオブジェクトに割り当てて呼び出すことができます。

call() は関数やメソッドに this の新しい値を提供します。 call() によって、いったんメソッドを書いてから、新しいオブジェクトへメソッドを書き直さずに他のオブジェクトへと継承することができます。

obj に対して _toString を呼び出しているようです。

_toString を見てみると Object.prototype.toString を呼び出しています。
vue/src/shared/util.ts#L49-L52 at main · vuejs/vue
スクリーンショット 2023-08-31 10.59.06.png

isPlainObject に戻ってみると、引数に対しtoString()を実行し、オブジェクトかどうかを判定しているという処理でした。
vue/src/shared/util.ts#L58-L64 at main · vuejs/vue
スクリーンショット 2023-08-31 10.56.56.png

今回の場合は、親コンポーネントで取得した HTMLDivElement をtoStirngした場合に、[object Object] となるのを期待してるようです。
実際に確認してみると [object HTMLDivElement] だったので、 Objectとしては判定されずエラーになっていました。

解決方法でエラーが出ない理由

今回の場合は

props: {
  element: {type: HTMLElement, required: true}
}

function assertType 内の isPlainObject より下に型をチェックする処理がありました。
vue/src/core/util/props.ts#L173-L175 at v2.7.14 · vuejs/vue
スクリーンショット 2023-08-31 11.22.18.png

つまり、element instanceof HTMLElementtrue となるのでエラーが出なくなったということでした。

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0