LoginSignup
2
0

More than 1 year has passed since last update.

zodでfirestoreのTimestamp型を取り扱うスキーマを作る

Last updated at Posted at 2022-08-21

動機

zodでFirestoreに格納するオブジェクトのスキーマを作るとき、Timestamp型のプロパティに指定するためのスキーマ定義が欲しい。

import { z } from "zod";

/** ユーザー情報スキーマ */
const userSchema = z.object({
  /** ユーザー名 */
  name: z.string().max(100).default(""),

  /** 最終ログイン日時 */
  lastLoginedAt: // 【ここに指定するスキーマは?】 
});

スキーマ定義

firebaseSchema.ts
import { z } from "zod";
import { FieldValue, Timestamp } from "firebase/firestore";

/** firestoreのFieldValueオブジェクトを受け付けるスキーマ */
export const firestoreFieldValueSchema = z.custom<FieldValue>(
  (data) => data instanceof FieldValue
);

/** firestoreのTimestampオブジェクトを受け付けるスキーマ(FieldValueは受け付けない) */
export const firestoreTimestampSchema = z.instanceof(Timestamp);

/** firestoreのTimestampオブジェクトまたはFieldValueオブジェクトを受け付けるスキーマ */
export const firestoreTimestampLooseSchema = z.union([
  firestoreFieldValueSchema,
  firestoreTimestampSchema,
]);

※Firebase v9以降を前提としています(v8以前はインターフェースが大きく異なるため、上記のソースはそのまま使用できないと思います)

使い方

厳密さを求めない場合は、下記のようにfirestoreTimestampLooseSchemaのみを使用すればOKです。
firestoreTimestampLooseSchemaは、Firestoreへデータを格納する時に設定する値(serverTimestamp() で生成した値)と、Firestoreからデータを取得したときに入っている値(Timestamp)の両方を受け付けてくれます。

import { z } from "zod";
import { firestoreTimestampLooseSchema } from "firebaseSchema";

/** ユーザー情報スキーマ */
const userSchema = z.object({
  /** ユーザー名 */
  name: z.string().max(100).default(""),

  /** 最終ログイン日時 */
  lastLoginedAt: firestoreTimestampLooseSchema,
});

/** ユーザー情報の型 */
export type User = z.infer<typeof userSchema>;
// => {
//     name: string;
//     lastLoginedAt: FieldValue | Timestamp;
// }

ただし上記のような定義だと、Firestoreから取得したデータの場合にも、lastLoginedAtフィールドの型がFieldValue | Timestampとなります。(この場合、FieldValueが入っている可能性はないはずです)
より厳密さを求めるなら、下記のように「未保存のデータ」と「保存済のデータ(Firestoreから取得したデータ)」を別々のスキーマとして定義してください。

import { z } from "zod";
import { firestoreTimestampLooseSchema, firestoreTimestampSchema } from "firebaseSchema";

/** ユーザー情報スキーマの共通プロパティ */
const baseSchema = z.object({
  /** ユーザー名 */
  name: z.string().max(100).default(""),
});

/** ユーザー情報スキーマ(保存済) Firestoreから取得したデータをパースする場合はこちらを使う */
export const storedUserSchema = baseSchema.extend({
  /** 最終ログイン日時 */
  lastLoginedAt: firestoreTimestampSchema,
});

/** ユーザー情報スキーマ(未保存) Firestoreへ格納するためのオブジェクトを生成する場合はこちらを使う */
export const unstoredUserSchema = baseSchema.extend({
  /** 最終ログイン日時 */
  lastLoginedAt: firestoreTimestampLooseSchema,
});

/** ユーザー情報の型 */
export type StoredUser = z.infer<typeof storedUserSchema>;
// => {
//     name: string;
//     lastLoginedAt: Timestamp;
// }

export type UnstoredUser = z.infer<typeof unstoredUserSchema>;
// => {
//     name: string;
//     lastLoginedAt: FieldValue | Timestamp;
// }

おまけ:Firebase v8以前向けのスキーマ定義(未検証)

注:ビルドエラーが出ないことは確認しましたが、動作は検証していないため、動くかどうかは分かりません。

import firebase from "firebase";
import { z } from "zod";

/** firestoreのFieldValueオブジェクトを受け付けるスキーマ */
export const firestoreFieldValueSchema =
  z.custom<firebase.firestore.FieldValue>(
    (data) => data instanceof firebase.firestore.FieldValue
  );

/** firestoreのTimestampオブジェクトを受け付けるスキーマ(FieldValueは受け付けない) */
export const firestoreTimestampSchema = z.instanceof(
  firebase.firestore.Timestamp
);

/** firestoreのTimestampオブジェクトまたはFieldValueオブジェクトを受け付けるスキーマ */
export const firestoreTimestampLooseSchema = z.union([
  firestoreFieldValueSchema,
  firestoreTimestampSchema,
]);

参考資料

FieldValueを受け付けるスキーマの定義については、GitHub内の下記Issueを参考にしています。

2
0
0

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
  3. You can use dark theme
What you can do with signing up
2
0