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
function getInvalidTypeMessage
の呼び出し元はこちらでした。
vue/src/core/util/props.ts#L135 at v2.7.14 · vuejs/vue
呼び出し元のfunction名を見てみると function assertProp
なので、prop関連についての処理を行ってそうです。
vue/src/core/util/props.ts#L105-L111 at v2.7.14 · vuejs/vue
そのまま読み進めてみると、assertType
を呼び出して型チェックをしていそうな箇所に行き着きました。
vue/src/core/util/props.ts#L126-L130 at v2.7.14 · vuejs/vue
function assertType
を読んでみると、期待する型がObjectの場合、isPlainObject
を呼び出して実際に渡された値がObjectかどうかの確認をしていそうです。
vue/src/core/util/props.ts#L168-L169 at v2.7.14 · vuejs/vue
function isPlainObject
を見てみます。
vue/src/shared/util.ts#L58-L64 at main · vuejs/vue
_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
isPlainObject
に戻ってみると、引数に対しtoString()を実行し、オブジェクトかどうかを判定しているという処理でした。
vue/src/shared/util.ts#L58-L64 at main · vuejs/vue
今回の場合は、親コンポーネントで取得した 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
つまり、element instanceof HTMLElement
が true
となるのでエラーが出なくなったということでした。