Firestore批量获取关联文档返回数组 修复TS Promise类型报错
Firestore关联一对一文档查询类型错误修复
问题场景
我的Firestore数据结构如下:topics 集合包含的文档结构如下:
id: string name: string tag: string infoID: string
上述文档对应 ITopic 接口:
interface ITopic { id: string name: string tag: string infoID: string }
infos 集合包含的文档结构如下:
id: string content: string topicID: string or null
上述文档对应如下接口:
interface IInfo { id: string content: string topicID: string | null }
每个 topic 文档都对应一个关联的 info 文档,二者为一对一映射关系。
我已经查询到所有 topic 文档,需要获取每个topic对应的关联info文档,最终组装为数组返回。数组每个元素为包含 topic 和 info 字段的对象,对应接口定义如下:
interface ITopicInfo { topic: ITopic info: IInfo }
原有实现代码如下(topics为已获取的所有topic文档数组):
async function getTopicsInfo(db: Firestore, topics:Array<ITopic>) { try { let topicsInfo:Array<ITopicInfo> = topics.map((topic) => { const infoRef = doc(db, 'infos', topic.infoID); const infoSnap = await getDoc(infoRef); if (infoSnap.exists()) { const result = infoSnap.data(); const newTopicInfo:ITopicInfo = { topic: topic, info: { id: result.id content: result.content topicID: result.topicID }, }; return newTopicInfo; } else return null; }); return topicsInfo; } catch (error) { throw error; } }
代码抛出TypeScript类型错误:Type '(Promise<ITopicTree | null> | null)[]' is not assignable to type '(ITopicTree | null)[]'.
- 错误本质是代码返回了Promise对象而非实际解析后的结果。
错误原因
Array.map不会自动等待内部异步操作执行,回调函数使用await时,map返回的结果是Promise对象组成的数组,和你声明的Array<ITopicInfo>类型不匹配,这是报错的核心原因。- 构造
info对象时字段之间遗漏逗号,存在基础语法错误。
修复方案
使用Promise.all包裹map返回的Promise数组,等待所有文档查询完成后再返回结果,同时修正语法问题:
async function getTopicsInfo(db: Firestore, topics: Array<ITopic>) { try { // map执行后得到Promise数组 const infoQueryPromises = topics.map(async (topic) => { const infoRef = doc(db, 'infos', topic.infoID); const infoSnap = await getDoc(infoRef); if (infoSnap.exists()) { const result = infoSnap.data() as IInfo; return { topic, info: { id: result.id, content: result.content, topicID: result.topicID } } satisfies ITopicInfo; } return null; }); // 等待所有查询完成,过滤查询失败返回null的项 const topicsInfo = (await Promise.all(infoQueryPromises)).filter(Boolean) as ITopicInfo[]; return topicsInfo; } catch (error) { throw error; } }
性能优化(可选)
如果topics数组长度较大,逐个发起文档查询会产生大量网络请求,可使用in查询批量拉取关联的info文档,注意Firestore单次in查询最多支持10个匹配值,超过10个需要分片查询:
async function getTopicsInfoOptimized(db: Firestore, topics: Array<ITopic>) { try { const infoIds = topics.map(t => t.infoID); // 数组长度超过10时需要自行实现分片逻辑 const infoQuery = query(collection(db, 'infos'), where(documentId(), 'in', infoIds)); const infoSnapshot = await getDocs(infoQuery); // 构造id到info的映射表,避免嵌套循环查找 const infoMap = new Map<string, IInfo>(); infoSnapshot.forEach(doc => { infoMap.set(doc.id, { id: doc.id, ...doc.data() } as IInfo); }); // 组装结果 return topics .map(topic => { const matchedInfo = infoMap.get(topic.infoID); return matchedInfo ? { topic, info: matchedInfo } : null; }) .filter(Boolean) as ITopicInfo[]; } catch (error) { throw error; } }
内容的提问来源于stack exchange,提问作者Sharon
相关产品推荐
相关产品推荐

