如何优化TypeScript中Firestore数据对象扩展运算符的类型处理
问题描述
我在从Firestore获取数据时编写了如下代码:
getDocs(colRef).then( (val) => { const tempArray: Array<categoryFetchData> = val.docs.map((d) => { const categoryData: {categoryName: string, color: string, createdAt: Timestamp} = d.data(); return {id: d.id, ...categoryData} } } )
d.data()的返回类型为DocumentData,但实际从集合获取的数据结构为{categoryName: "someString", color: "someColor", createdAt: Timestamp.now()}。
我的函数返回类型为Array<categoryFetchData>,其定义如下:
type categoryFetchData = { categoryName: string, color: string, createdAt: Timestamp, id: string }
在尝试将d.data()扩展到返回值时,出现错误:
Type 'DocumentData' is missing the following properties from type '{ categoryName: string; color: string; createdAt: Timestamp; }': categoryName, color, createdAt
目前我通过以下方式修复:
type ExpenseCategoryStruct = {categoryName: string; color: string; createdAt: Timestamp;}; const categoryData = d.data() as ExpenseCategoryStruct;
请问是否存在无需创建新变量并使用as类型断言的更优解决方法?
解决方案
1. 直接在展开时做类型断言(无需额外变量)
不用单独声明categoryData变量,直接在展开语法中对d.data()做类型断言,精简代码:
getDocs(colRef).then( (val) => { const tempArray: Array<categoryFetchData> = val.docs.map((d) => ({ id: d.id, ...(d.data() as Omit<categoryFetchData, 'id'>) })); } )
这里用Omit<categoryFetchData, 'id'>复用已有类型,避免重复定义结构。
2. 使用Firestore泛型getDocs<T>(无断言,类型自动推导)
Firestore的getDocs支持泛型参数,提前指定文档类型后,d.data()会自动返回对应类型,无需手动断言:
// 复用已有类型,提取不含id的文档结构 type ExpenseCategoryStruct = Omit<categoryFetchData, 'id'>; getDocs<ExpenseCategoryStruct>(colRef).then( (val) => { const tempArray: Array<categoryFetchData> = val.docs.map((d) => ({ id: d.id, ...d.data() // 此处d.data()类型已匹配,无报错 })); } )
这种方式最优雅,只要你确认集合中文档结构符合定义,就可以安全使用。
3. 类型守卫(运行时验证数据,更安全)
如果需要确保数据结构符合预期,可编写类型守卫函数在运行时校验,避免脏数据导致问题:
function isExpenseCategory(data: DocumentData): data is Omit<categoryFetchData, 'id'> { return ( typeof data.categoryName === 'string' && typeof data.color === 'string' && data.createdAt instanceof Timestamp ); } getDocs(colRef).then( (val) => { const tempArray: Array<categoryFetchData> = val.docs.map((d) => { const data = d.data(); if (!isExpenseCategory(data)) { throw new Error('Firestore返回的分类数据结构不符合预期'); } return {id: d.id, ...data}; }); } )
这种方式代码量稍大,但能在运行时拦截无效数据,适合对数据可靠性要求高的场景。
内容的提问来源于stack exchange,提问作者Fergus Johnson
相关产品推荐
相关产品推荐

