input の value の型は 9 割 string 1 割 boolean

2021-04-06

よく勘違いしている人がいますが inputvalue は基本的に string しか取り得ることができません。

import React, { FC, useCallback } from "react"; import { useForm } from "react-hook-form"; type FieldValues = { hoge: string; }; const App: FC = () => { const { register, handleSubmit } = useForm<FieldValues>(); const onSubmit = useCallback(({ hoge }: FieldValues) => { // true console.log(typeof hoge === "string"); }, []); return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register("hoge")} /> <button type="submit">submit</button> </form> ); }; export default App;

なぜ string しか取り得ることができないかというと、HTML ドキュメントに value を埋め込む以上、文字列しかしか扱うことができないからです。

たまに value の型を optional にしているケースを目にしますが、これは推奨される書き方ではありません。

import React, { FC, useCallback } from "react"; import { useForm } from "react-hook-form"; type FieldValues = { // 推奨されない書き方 hoge?: string; }; const App: FC = () => { const { register, handleSubmit } = useForm<FieldValues>({ defaultValues: { // もし初期値に undefined を与えたとしても hoge: undefined, }, }); const onSubmit = useCallback(({ hoge }: FieldValues) => { // 型としては undefined を許容しているが value に undefined が格納されることはない // 何も入力されていない場合、空文字が格納される // true console.log(typeof hoge === "string"); }, []); return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register("hoge")} type="number" /> <button type="submit">submit</button> </form> ); }; export default App;

当たり前といえば当たり前なんですが、HTML ドキュメントが文字列以外扱うことができない以上 undefinednull を扱うことはできません。

仮に typenumber を与えたとしても、基本的に value の型は string です。

import React, { FC, useCallback } from "react"; import { useForm } from "react-hook-form"; type FieldValues = { // 推奨されない書き方 hoge: number; }; const App: FC = () => { const { register, handleSubmit } = useForm<FieldValues>(); const onSubmit = useCallback(({ hoge }: FieldValues) => { // 型としては number だが格納される値は string となってしまう // true console.log(typeof hoge === "string"); }, []); return ( <form onSubmit={handleSubmit(onSubmit)}> {/* type に number を与えたとしても */} <input {...register("hoge")} type="number" /> <button type="submit">submit</button> </form> ); }; export default App;

ただしややこしいのが初期値に number を与えた場合、初期値から値を変えなかった場合のみ valuenumber として扱われてしまいました。

import React, { FC, useCallback } from "react"; import { useForm } from "react-hook-form"; type FieldValues = { // 推奨されない書き方 hoge: number; }; const App: FC = () => { const { register, handleSubmit } = useForm<FieldValues>({ defaultValues: { hoge: 0, }, }); const onSubmit = useCallback(({ hoge }: FieldValues) => { // value を初期値から変更していない場合のみ false // それ以外の場合 true console.log(typeof hoge === "string"); }, []); return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register("hoge")} type="number" /> <button type="submit">submit</button> </form> ); }; export default App;

バグとも仕様ともなんとも言えない挙動ですが、value の型を string にしてやれば済む話なので気をつけましょう。

ただし、typecheckbox の場合のみ、value の型は boolean にしましょう。

import React, { FC, useCallback } from "react"; import { useForm } from "react-hook-form"; type FieldValues = { hoge: boolean; }; const App: FC = () => { const { register, handleSubmit } = useForm<FieldValues>(); const onSubmit = useCallback(({ hoge }: FieldValues) => { // true console.log(typeof hoge === "boolean"); }, []); return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register("hoge")} type="checkbox" /> <button type="submit">submit</button> </form> ); }; export default App;

checkbox の場合は checked か否かの 2 択であるため、パッケージ内でよしなに変換してくれているようです。

きちんと扱えていないケースをよく目にしてきたので、備忘録がてら。

© 2018 kk-web