You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 22:48:19