JavaScriptでFirestoreにDateを入れるとTimestampに変換される
React Native Expo に Firestore を導入して開発していたところ、Date を入れたときに Timestamp 型へと変換されてしまうことに気付きました。
Firestore で JavaScript の Date 型を使いたい場合は、WithConverter で Timestamp 型から Date 型に戻してあげる必要があります。
Firebase Timestamp
Firebase のドキュメントを見ると、Timestamp 型はナノ秒の精度があり、Date 型に変換する I/F を持っています。
Date toDate() Returns a new Date corresponding to this timestamp.
Firestore からの読み込み時に処理を挟む WithConverter
withConverter を使うと、get()の手前にコンバータを挟むことができます。
const tasksSnapshot = await dbh
.collection("users")
.doc(userId)
.collection("tasks")
.withConverter(Task.converter)
.get();
次に、コンバータの中身を定義します。Timestamp.toDate()を使って Date 型に変換し、他のプロパティは特に何もせずそのまま return しています。
export const converter = {
fromFirestore: function (
snapshot: firebase.firestore.QueryDocumentSnapshot<ModelFromFirestore>,
options: firebase.firestore.SnapshotOptions
): Model {
const { schedule, ...data } = snapshot.data();
const date = schedule.toDate();
return {
schedule: date,
...data,
};
},
};
Firestore に入れる前の型と取り出した後の型が変わります。TypeScript の場合は変わる前後両方の方を定義してあげます。
export interface Model {
id: string;
name: string;
schedule: Date;
}
export interface ModelFromFirestore {
id: string;
name: string;
schedule: firebase.firestore.Timestamp;
}
ModelFromFirestore を dry に書く
上記のコードの Model と ModelFromFirestore はほとんど同じなので dry に書きたいところですが、プロパティの型を変更して継承させることはできません。なので、以下のように中継型を作ってあげると良いです。
// 指定したプロパティを上書きして継承するための中継型
export type Weaken<T, K extends keyof T> = {
[P in keyof T]: P extends K ? any : T[P];
};
export interface ModelFromFirestore extends Util.Weaken<Model, "schedule"> {
schedule: firebase.firestore.Timestamp;
}
中継型を乱用すると良いこと無さそうですが、今回のような用途であれば OK かと思います。
無事に Timestamp を Date に変換できました。
私が作業中に飲んでいるコーヒーや欲しいマンガなどを集めました。開発・執筆の励みになりますのでクリックして頂ければ幸いです。
<Amazon欲しいものリスト>