はじめに
Yupを業務で使用しているときにnullable()
という機能があることを知りました。
当初、nullを設定できるようにするためのものかと思っていましたが、それであるならばrequired()
をつけなければ済む話です。
では、nullable()
がどのような場合に有効なのかを調べてみました。
APIのレスポンスを受け取る場合を考える
ユーザー情報を編集するフォームを例にして、APIから以下のようなレスポンスを受け取る場合を考えてみます。
ユーザーID、ユーザー名、マネージャーIDを受け取ることを考えます。
{
"id": 2,
"name": "山田花子",
"managerId": 1 // マネージャーが設定されている場合
}
// または
{
"id": 1,
"name": "田中太郎",
"managerId": null // マネージャーが未設定の場合
}
// 型定義
interface User {
id: number;
name: string;
managerId: number | null;
}
マネージャーは未設定な場合も考えられるものとし、その場合にはnull
が設定されるものとします。
次に、Yupのスキーマ定義を考えてみましょう。
次の2つのパターンを比較してみます。
// パターン1: nullable()を使用しない場合
const schemaWithoutNullable = yup.object({
name: yup.string().required(),
managerId: yup.number()
});
// パターン2: nullable()を使用する場合
const schemaWithNullable = yup.object({
name: yup.string().required(),
managerId: yup.number().nullable()
});
// 型の取り出し
type InferredType1 = yup.InferType<typeof schemaWithoutNullable>;
type InferredType2 = yup.InferType<typeof schemaWithNullable>;
ここで、重要な違いが出てきます。
// パターン1の型
type InferredType1 = {
name: string;
managerId: number | undefined; // undefinedは許容されるがnullは許容されない
}
// パターン2の型
type InferredType2 = {
name: string;
managerId: number | null | undefined; // nullが設定可能
}
// 型の整合性チェック
const updateUser = (data: User) => {
// パターン1の場合:型エラー
const form1: InferredType1 = {
name: data.name,
managerId: data.managerId // Error: 'null' is not assignable to 'number | undefined'
};
// パターン2の場合:OK
const form2: InferredType2 = {
name: data.name,
managerId: data.managerId // OK: 型が一致する
};
};
nullableを設定しなかったほうは、numberまたはundefinedとされ、nullを設定することができません。
nullableを設定したほうは、nullを問題なく設定することができるようになっています。
このように、nullが入る可能性のあるフォームに対して、nullablle
を設定することで、型定義の整合性を保つことができるようになります。
まとめ
nullable
を設定することで、推論される型にnullが追加されることがわかりました。
明示的にnullを設定したり、外部APIと連携する際にnullが設定される可能性がある項目に対してnullableを指定するのが良さそうです。