如何为TypeScript中Firestore集合操作函数添加正确类型约束?
基于Firestore的TypeScript类型约束实现
现有基础代码
已完成Firestore数据模型的TypeScript定义,以及支持嵌套集合路径的参数类型:
数据模型定义
// 简化的业务类型 type ChatMessage = { message: string } type Chat = {} // 原定义省略 type ChatParticipant = { name: string } type FirestoreCollection<T> = { documentType: T; subcollections?: { [key: string]: FirestoreCollection<object>; }; }; type FirestoreSchema< T extends { [key: string]: U }, U = FirestoreCollection<object> > = T; export type FirestoreModel = FirestoreSchema<{ 'chat-messages': { documentType: ChatMessage; }; chats: { documentType: Chat; subcollections: { 'chat-participants': { documentType: ChatParticipant; }; }; }; }>;
集合路径参数类型
已实现支持顶级/嵌套集合路径的参数类型:
type GetCollectionArgs<T = FirestoreModel> = { [K in keyof T]: | [{ collection: K; id: string }] | ('subcollections' extends keyof T[K] ? [ { collection: K; id: string }, ...GetCollectionArgs<T[K]['subcollections']> ] : never); }[keyof T]; // 现有getDocument函数(未指定返回类型) function getDocument(...args: GetCollectionArgs<FirestoreModel>) { // 业务逻辑实现 }
需求说明
- getDocument返回类型约束:根据传入的集合路径,自动推导返回对应集合的
documentType类型 - updateDocument类型校验:确保第一个参数的数据类型与指定集合路径的
documentType完全匹配,类型不匹配时触发TypeScript报错
解决方案
1. 实现getDocument的返回类型推导
新增递归辅助类型GetDocumentType,用于根据路径提取对应的documentType,并修改getDocument的泛型定义:
// 递归提取路径对应的documentType type GetDocumentType<T, Path extends any[]> = // 路径为单级集合 Path extends [{ collection: infer K; id: string }] ? T[K]['documentType'] : // 路径为多级嵌套集合 Path extends [{ collection: infer K; id: string }, ...infer Rest] ? 'subcollections' extends keyof T[K] ? GetDocumentType<T[K]['subcollections'], Rest> : never : never; // 更新getDocument函数定义 function getDocument<Path extends GetCollectionArgs<FirestoreModel>>( ...args: Path ): GetDocumentType<FirestoreModel, Path> { // 原有业务逻辑 } // 验证: // 返回类型为ChatMessage getDocument({ collection: 'chat-messages', id: '123' }); // 返回类型为ChatParticipant getDocument( { collection: 'chats', id: '123' }, { collection: 'chat-participants', id: '456' } );
2. 实现updateDocument的类型校验
利用已有的GetCollectionArgs和GetDocumentType,通过泛型关联数据参数与路径参数的类型:
function updateDocument<Path extends GetCollectionArgs<FirestoreModel>>( data: GetDocumentType<FirestoreModel, Path>, ...args: Path ): void { // 业务逻辑实现 } // 验证: // 正常运行(data匹配ChatMessage) updateDocument( { message: 'hello' }, { collection: 'chat-messages', id: '123' } ); // 正常运行(data匹配ChatParticipant) updateDocument( { name: 'ryan' }, { collection: 'chats', id: '123' }, { collection: 'chat-participants', id: '456' } ); // 触发类型错误(data是ChatParticipant类型,路径对应ChatMessage) updateDocument( { name: 'ryan' }, { collection: 'chat-messages', id: '123' } );
内容的提问来源于stack exchange,提问作者Ryan
相关产品推荐
相关产品推荐

