如何从TypeScript泛型Hook中获取特定类型?
问题
我正尝试创建一个连接Firebase/Firestore的泛型Hook,用于返回指定集合的所有数据项。我通过创建ICollectionMap接口来指定各集合数据的类型,现有代码如下:
export const useCollection = <TCollectionMap extends Record<string, any>>( collectionName: keyof TCollectionMap ): { collection: TCollectionMap[keyof TCollectionMap] } => { const [collection, setCollection] = useState<TCollectionMap[keyof TCollectionMap]>([]) useEffect(() => { // fetch data from Firebase here ... const data = ... setCollection(data) }, []) return { collection } }
定义以下类型:
interface INames { name: string } interface IAges { age: number } interface ICollectionMap { names: INames[] ages: IAges[] }
当我调用该Hook时:
const { collection } = useCollection<ICollectionMap>('names')
我期望collection的类型为INames[],但TypeScript显示其类型为INames[] | IAges[]。请问如何用TypeScript正确处理这个问题?
解决方案
问题核心是当前泛型仅约束了集合映射的整体类型,未将传入的collectionName与返回的集合类型做精准关联。通过新增一个泛型参数锁定具体集合键,即可让TypeScript推导出对应类型:
修改后的Hook代码:
import { useState, useEffect } from 'react'; export const useCollection = < TCollectionMap extends Record<string, any>, TKey extends keyof TCollectionMap >( collectionName: TKey ): { collection: TCollectionMap[TKey] } => { const [collection, setCollection] = useState<TCollectionMap[TKey]>([] as TCollectionMap[TKey]) useEffect(() => { // 示例:模拟Firebase数据请求 const fetchData = async () => { // 实际项目替换为Firestore集合查询逻辑 const snapshot = await fetch(`/api/${collectionName}`); const data = await snapshot.json(); setCollection(data); }; fetchData(); }, [collectionName]) return { collection } }
调用时的类型推导完全符合预期:
// 手动指定泛型参数的写法 const { collection } = useCollection<ICollectionMap, 'names'>('names'); // collection类型为INames[] // TypeScript可自动推导TKey参数,无需手动指定 const { collection } = useCollection<ICollectionMap>('names'); // 同样得到INames[]类型
关键改动说明
- 新增泛型参数
TKey extends keyof TCollectionMap,明确绑定传入的集合名称对应的键类型 - 返回类型与
useState泛型均改为TCollectionMap[TKey],让TypeScript精准关联集合名与对应数据类型 - 初始值通过
[] as TCollectionMap[TKey]做类型断言,避免空数组的类型不匹配问题
内容的提问来源于stack exchange,提问作者Bartek
相关产品推荐
相关产品推荐

