如何改进Firestore通用转换器以支持嵌套自定义对象?
支持嵌套自定义对象的Firebase通用转换器实现
当然可以改进通用转换器来支持嵌套自定义对象,核心思路是递归处理嵌套属性,同时解决undefined值导致的失效问题。以下是具体实现方案:
关键改进点
- 递归序列化/反序列化:遍历对象属性时,识别自定义类实例并递归转换
- 处理undefined值:Firestore不允许存储
undefined,序列化时自动过滤;反序列化时给缺失字段设为undefined - 类型映射支持:通过类的静态属性标记嵌套字段对应的构造函数,让转换器知道如何实例化嵌套对象
完整实现代码
1. 定义带类型标记的业务类
给嵌套类添加静态属性__nestedTypes,标记字段对应的构造函数:
class Country { name?: string; language?: string; // 标记当前类的嵌套类型(无嵌套时可省略) static __nestedTypes: Record<string, new () => any> = {}; } class City { name?: string; state?: string; country?: Country; // 标记嵌套字段对应的构造函数 static __nestedTypes: Record<string, new () => any> = { country: Country }; }
2. 改进后的通用转换器
import { DocumentData, QueryDocumentSnapshot } from "firebase/firestore"; class FirestoreConverter<T extends object> { private rootType: new () => T; constructor(rootType: new () => T) { this.rootType = rootType; } toFirestore(dto: T): DocumentData { const data: DocumentData = {}; Object.keys(dto).forEach(key => { const value = Reflect.get(dto, key); // 过滤undefined值,避免Firestore写入报错 if (value === undefined) return; // 递归处理自定义类实例 if (value instanceof Object && !(value instanceof Array) && value.constructor.__nestedTypes) { const nestedConverter = new FirestoreConverter(value.constructor as new () => typeof value); data[key] = nestedConverter.toFirestore(value); } else { data[key] = value; } }); return data; } fromFirestore(snapshot: QueryDocumentSnapshot<T>): T { const data = snapshot.data() as DocumentData; const dto = new this.rootType() as T; const nestedTypes = (this.rootType as any).__nestedTypes || {}; Object.keys(data).forEach(key => { const value = data[key]; const nestedConstructor = nestedTypes[key]; // 递归实例化嵌套对象 if (nestedConstructor && value instanceof Object && !(value instanceof Array)) { const nestedConverter = new FirestoreConverter(nestedConstructor); Reflect.set(dto, key, nestedConverter.fromFirestore({ data: () => value } as QueryDocumentSnapshot<any>)); } else { Reflect.set(dto, key, value ?? undefined); } }); // 处理数据中缺失的字段,统一设为undefined Object.keys(dto).forEach(key => { if (!(key in data)) { Reflect.set(dto, key, undefined); } }); return dto; } }
3. 使用方式
// 创建转换器实例,传入根类构造函数 const cityConverter = new FirestoreConverter(City); // 写入Firestore const city = new City(); city.name = "New York"; city.country = new Country(); city.country.name = "USA"; await db.collection("cities").doc("ny").withConverter(cityConverter).set(city); // 读取Firestore const doc = await db.collection("cities").doc("ny").withConverter(cityConverter).get(); const retrievedCity = doc.data(); console.log(retrievedCity?.country?.name); // 输出 "USA"
补充说明
- 若嵌套类层级更深,转换器会自动递归处理,无需额外修改
- 对于数组类型的嵌套对象(比如
cities: City[]),可在toFirestore和fromFirestore中增加数组遍历逻辑,递归转换每个元素 - 静态属性
__nestedTypes可以用装饰器简化,比如写一个@Nested(Country)装饰器标记字段,让代码更简洁
内容的提问来源于stack exchange,提问作者Foxhunt
相关产品推荐
相关产品推荐

