如何让React TypeScript Hook根据集合路径自动推断类型?
让React Hook根据传入参数自动推断返回类型
我写了下面这个React Hook:
import { userDB } from 'app/services/DBFunctions'; import { auth } from 'Fire'; import { collection, query, onSnapshot } from 'firebase/firestore'; import { IScheduledEmail, IScheduledInvoice } from 'Interface'; import React, { useEffect, useState } from 'react'; interface Props { collectionPath: 'scheduled-invoices' | 'scheduled-emails' } const useScheduledFunctions = (props: Props) : IScheduledInvoice[] | IScheduledEmail[] => { const {collectionPath} = props const [scheduledFunctions, setScheduledFunctions] = useState<IScheduledInvoice[] | IScheduledEmail[]>([]) const user = auth.currentUser useEffect(()=> { let queryCollection = query( collection(userDB(), collectionPath), ) onSnapshot(queryCollection, (snap: any)=> { let scheduledFunctions = snap.docs.map((doc: any)=> doc.data()) setScheduledFunctions(scheduledFunctions) }) }, [user, collectionPath]) return scheduledFunctions }; export default useScheduledFunctions;
现在调用这个Hook的时候,返回类型是IScheduledInvoice[] | IScheduledEmail[]的联合类型,我必须用as IScheduledEmail[]手动断言类型,但这不是最优解。有没有办法让Hook根据传入的collectionPath自动推断对应的返回类型?
可以通过泛型+映射类型实现自动类型推断,具体修改如下:
1. 定义路径与类型的映射关系
首先创建一个映射类型,将每个collection路径和对应的实体类型绑定:
// 关联collection路径和对应的数据类型 type CollectionTypeMap = { 'scheduled-invoices': IScheduledInvoice; 'scheduled-emails': IScheduledEmail; }; // 提取合法的路径枚举类型 type CollectionPath = keyof CollectionTypeMap;
2. 重构Hook为泛型版本
修改Hook的定义,利用泛型让TypeScript根据传入的collectionPath自动推断返回类型:
import { userDB } from 'app/services/DBFunctions'; import { auth } from 'Fire'; import { collection, query, onSnapshot } from 'firebase/firestore'; import { IScheduledEmail, IScheduledInvoice } from 'Interface'; import React, { useEffect, useState } from 'react'; type CollectionTypeMap = { 'scheduled-invoices': IScheduledInvoice; 'scheduled-emails': IScheduledEmail; }; type CollectionPath = keyof CollectionTypeMap; const useScheduledFunctions = <T extends CollectionPath>(collectionPath: T): CollectionTypeMap[T][] => { const [scheduledFunctions, setScheduledFunctions] = useState<CollectionTypeMap[T][]>([]) const user = auth.currentUser useEffect(()=> { // 增加用户空值判断,避免未登录时调用userDB出错 if (!user) return; const queryCollection = query(collection(userDB(), collectionPath)); // 订阅快照,记得返回清理函数防止内存泄漏 const unsubscribe = onSnapshot(queryCollection, (snap)=> { // 断言数据类型,因为我们通过映射确保了路径和类型的对应关系 const data = snap.docs.map(doc => doc.data() as CollectionTypeMap[T]); setScheduledFunctions(data); }) return unsubscribe; }, [user, collectionPath]) return scheduledFunctions }; export default useScheduledFunctions;
3. 调用示例
现在调用Hook时,TypeScript会自动根据传入的路径推断返回类型:
// 自动推断返回类型为 IScheduledInvoice[] const invoices = useScheduledFunctions('scheduled-invoices'); // 自动推断返回类型为 IScheduledEmail[] const emails = useScheduledFunctions('scheduled-emails');
关键修改说明
- 用
CollectionTypeMap建立路径与类型的强关联,让TS能通过路径定位到对应类型; - 泛型
T extends CollectionPath约束了传入路径的合法性,同时为类型推断提供依据; - 增加了用户空值判断和快照订阅的清理函数,提升Hook的健壮性;
doc.data()的类型断言是安全的,因为我们已经通过映射确保了路径对应的数据类型符合预期。
内容的提问来源于stack exchange,提问作者User123123
相关产品推荐
相关产品推荐

