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

TypeScript:如何获取联合类型的具体typeof类型?

问题解决:React Hook中处理不同数组类型的类型窄化

你的问题核心在于泛型约束与类型关联不紧密,导致TypeScript无法在switch语句中正确推断array的具体类型,同时状态filteredData的类型固定为Chat[]也会引发类型不匹配错误。以下是具体的修复方案:

问题分析

  1. 泛型PossibleType没有明确的约束范围,TypeScript无法建立type参数和array类型的强关联;
  2. filteredData的状态类型固定为Chat[],当传入NewGroupMember[]时会出现类型不兼容;
  3. switch语句中,TypeScript无法通过type的值自动窄化array的类型,导致访问chatName或email时出现类型错误。

解决方案:使用函数重载+类型窄化

函数重载能明确不同参数组合对应的返回值类型,让TypeScript准确推断类型,是处理此类多类型输入场景的最优方案之一。

修复后的完整代码

import { useState, useEffect } from 'react';
import useDebounce from './useDebounce'; // 假设你的防抖Hook路径正确

// 确保已正确定义数据类型
interface Chat {
  chatName: string;
  // 其他属性...
}

interface NewGroupMember {
  email: string;
  // 其他属性...
}

// 1. 定义函数重载签名,明确参数与返回值的类型关联
export function useSort(delay: number, array: Chat[], type: "MainPanel.tsx"): {
  rawInput: string;
  handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
  delayedInput: string;
  filteredData: Chat[];
};

export function useSort(delay: number, array: NewGroupMember[], type: "NewGroup.tsx"): {
  rawInput: string;
  handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
  delayedInput: string;
  filteredData: NewGroupMember[];
};

// 2. 实现函数逻辑,TypeScript会自动根据重载推断类型
export function useSort(
  delay: number,
  array: Chat[] | NewGroupMember[],
  type: "MainPanel.tsx" | "NewGroup.tsx"
) {
  const [rawInput, setRawInput] = useState("");
  // 根据输入array的类型初始化状态,避免类型不匹配
  const [filteredData, setFilteredData] = useState(array);

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => 
    setRawInput(e.target.value.toLowerCase());

  const delayedInput = useDebounce(rawInput, delay);

  useEffect(() => {
    if (!delayedInput || delayedInput.length <= 1) {
      setFilteredData(array);
      return;
    }

    let result: typeof array;

    switch (type) {
      case "MainPanel.tsx":
        // TypeScript自动窄化array为Chat[],无需额外断言
        result = array.filter((data) => data.chatName.includes(delayedInput));
        break;
      case "NewGroup.tsx":
        // 同理,自动窄化为NewGroupMember[]
        result = array.filter((data) => data.email.includes(delayedInput));
        break;
      default:
        result = array;
    }

    setFilteredData(result);
  }, [delayedInput, array, type]);

  return { rawInput, handleChange, delayedInput, filteredData };
}

关键优化点

  • 函数重载:通过重载签名明确type参数与array类型的一一对应关系,TypeScript能在调用时自动校验参数类型,并在实现中正确窄化类型;
  • 状态类型动态化:将filteredData的初始值设为传入的array,让状态类型自动匹配输入类型,避免固定类型导致的错误;
  • 类型安全的过滤逻辑:switch中TypeScript会根据type的值自动推断array的具体类型,无需手动类型断言;
  • 替换search为includes:更直观地判断字符串是否包含目标值,避免search返回索引的额外判断。

替代方案:泛型+条件类型

如果你更倾向于用泛型实现,可以通过条件类型绑定type和array的关系:

type SortType = "MainPanel.tsx" | "NewGroup.tsx";
type ArrayForType<T extends SortType> = T extends "MainPanel.tsx" ? Chat[] : NewGroupMember[];

export function useSort<T extends SortType>(
  delay: number,
  array: ArrayForType<T>,
  type: T
) {
  const [rawInput, setRawInput] = useState("");
  const [filteredData, setFilteredData] = useState<ArrayForType<T>>(array);

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => 
    setRawInput(e.target.value.toLowerCase());

  const delayedInput = useDebounce(rawInput, delay);

  useEffect(() => {
    if (!delayedInput || delayedInput.length <= 1) {
      setFilteredData(array);
      return;
    }

    let result: ArrayForType<T>;

    switch (type) {
      case "MainPanel.tsx":
        result = (array as Chat[]).filter(data => data.chatName.includes(delayedInput));
        break;
      case "NewGroup.tsx":
        result = (array as NewGroupMember[]).filter(data => data.email.includes(delayedInput));
        break;
      default:
        result = array;
    }

    setFilteredData(result);
  }, [delayedInput, array, type]);

  return { rawInput, handleChange, delayedInput, filteredData };
}

这种方式需要手动添加类型断言,适合需要复用类型逻辑的场景。

内容的提问来源于stack exchange,提问作者Itgelt Gankhulug

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 04:10:34