Firestore.Timestamp って、いらなくね?
ちゃんと使いこなせばメリットもあるんだろうなぁと思いつつ・・・
少なくとも小規模なプロジェクトでは、Timestamp型が存在するせいで
- Firestore から読んだ、時刻フィールドを toDate() で Date型にもどす
- Document の interface と、 REST をまたぐ interface を分ける
みたいな対応が必要になるわけです。いちいちめんどいんすこれ。
Date 型で十分な状況もあるんです。
問題の例
interface User {
name: string;
updatedAt: Date;
}
const write: User = {
name: "taro",
updatedAt: Date.now(),
};
await firestore
.collection("users")
.doc(write.name)
.set(write);
const read: User =
await firestore
.collection("users")
.doc(write.name)
.get()
.then(dss=>dss.data());
console.log(read); // updatedAt が Timestamp クラスになってるよ!
魔術の源: Timestamp を Date へ再帰的に変換するヘルパーメソッド
function timestampToDateRecursively(value: any): any{
if (value == null) {
return value;
} else if (value.constructor === Firestore.Timestamp) {
return value.toDate();
} else if (Array.isArray(value)) {
return value.map(timestampToDateRecursively);
} else if (value.constructor === Object) {
const converted: any = {};
for (const key in value) {
converted[key] = timestampToDateRecursively(value[key]);
}
return converted;
} else {
return value;
}
}
白魔術: いちいちヘルパーメソッドをかませる
- どこで何が起きてるかわすりやすい。安全。
- いちいち忘れたりする。めんどい。
const read: User =
await firestore
.collection("users")
.doc(write.name)
.get()
.then(dss=>timestampToDateRecursively(dss.data())); // 毎回書く必要あり
console.log(read); // updatedAt がちゃんと Date になってるね
黒魔術: DocumentSnapshot.data() をオーバーライドする
- 意識しなくても勝手に変換されてる。便利。
- 知らないと、Firestoreの仕様の誤解や混乱のもとになる。危険。
/**
* DocumentSnapShot.data() で返すすべてのTimestamp型をあらかじめDate型へ変換するよう
* プロトタイプをオーバーライドします
*/
function wrapDocumentSnapshotData() {
console.log(`Wrapping DocumentSnapshot.data()`);
const origin = Firestore.DocumentSnapshot.prototype.data;
Firestore.DocumentSnapshot.prototype.data = function () {
const data = origin.bind(this)();
const converted = timestampToDateRecursively(data); // ここ!
return converted;
};
}
wrapDocumentSnapshotData();
const read: User =
await firestore
.collection("users")
.doc(write.name)
.get()
.then(dss=>dss.data()); // いちいち意識しなくていい
console.log(read); // updatedAt がちゃんと Date になってるね