input の value の型は 9 割 string 1 割 boolean
2021-04-06
よく勘違いしている人がいますが input
の value
は基本的に 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 ドキュメントが文字列以外扱うことができない以上 undefined
や null
を扱うことはできません。
仮に type
に number
を与えたとしても、基本的に 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
を与えた場合、初期値から値を変えなかった場合のみ value
が number
として扱われてしまいました。
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
にしてやれば済む話なので気をつけましょう。
ただし、type
が checkbox
の場合のみ、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 択であるため、パッケージ内でよしなに変換してくれているようです。
きちんと扱えていないケースをよく目にしてきたので、備忘録がてら。