TypeScript:如何获取联合类型的具体typeof类型?
问题解决:React Hook中处理不同数组类型的类型窄化
你的问题核心在于泛型约束与类型关联不紧密,导致TypeScript无法在switch语句中正确推断array的具体类型,同时状态filteredData的类型固定为Chat[]也会引发类型不匹配错误。以下是具体的修复方案:
问题分析
- 泛型
PossibleType没有明确的约束范围,TypeScript无法建立type参数和array类型的强关联; filteredData的状态类型固定为Chat[],当传入NewGroupMember[]时会出现类型不兼容;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
相关产品推荐
相关产品推荐

