如何让TypeScript根据枚举动态推导回调的对象类型?
TypeScript动态推导回调参数类型问题解决
问题场景
给定以下TypeScript代码:
enum stores { book, chapter, verse } const reducer = { [stores.book]: { a: 'string', b: 1 }, [stores.chapter]: { a: 'string', c: 2 }, [stores.verse]: { a: 'string', d: false } } function main ({ storeName, getA }:{ storeName: stores, getA: (item: typeof reducer[stores]) => void }) { const item = reducer[storeName] getA(item) return } main({storeName: stores.verse, getA(item) { item.a // 仅能访问公共属性a,但 item.d // 无法访问stores.verse特有的属性d }})
当前getA回调仅能识别item的公共属性a,无法根据传入的storeName动态识别对应对象的特有属性。需要让TypeScript根据storeName和reducer动态推导getA中item的准确类型,该场景用于React-Redux通用组件开发,需在props函数中获取Redux切片对象的精确类型。
解决方案
核心思路是用泛型绑定storeName与getA参数的类型关联,让TypeScript根据传入的storeName自动推导对应切片的精确类型。
优化后的基础代码
enum stores { book, chapter, verse } const reducer = { [stores.book]: { a: 'string', b: 1 }, [stores.chapter]: { a: 'string', c: 2 }, [stores.verse]: { a: 'string', d: false } } // 给main函数添加泛型S,约束为stores枚举的成员 function main<S extends stores>({ storeName, getA }: { storeName: S, // 让getA的参数类型对应reducer中S对应的具体类型 getA: (item: typeof reducer[S]) => void }) { const item = reducer[storeName] getA(item) return } main({ storeName: stores.verse, getA(item) { item.a // 正常访问公共属性 item.d // 现在可正确识别并访问stores.verse特有的属性d } }) main({ storeName: stores.book, getA(item) { item.b // 可访问book特有的属性b } })
原理说明
- 泛型
S约束为stores枚举成员,确保storeName只能传入枚举内的有效值; - 将
getA的参数类型指定为typeof reducer[S],TypeScript会根据storeName对应的泛型S,自动关联reducer中对应键的具体类型,实现动态推导; - 调用
main时,传入不同storeName,TypeScript会自动推导出getA参数的精确类型,支持访问对应切片的特有属性。
React-Redux场景适配
在React-Redux通用组件中,可将泛型逻辑应用到组件Props定义:
import { useSelector } from 'react-redux'; // 定义根状态类型,包含所有切片 type RootState = { [stores.book]: typeof reducer[stores.book], [stores.chapter]: typeof reducer[stores.chapter], [stores.verse]: typeof reducer[stores.verse] }; // 泛型组件Props type GenericComponentProps<S extends stores> = { storeName: S; onSliceData: (data: RootState[S]) => void; }; function GenericComponent<S extends stores>({ storeName, onSliceData }: GenericComponentProps<S>) { const sliceData = useSelector((state: RootState) => state[storeName]); const handleTrigger = () => { onSliceData(sliceData); }; return <button onClick={handleTrigger}>获取切片数据</button>; } // 使用组件时自动推导类型 <GenericComponent storeName={stores.chapter} onSliceData={(data) => { console.log(data.c); // 正确识别chapter特有的属性c }} />
内容的提问来源于stack exchange,提问作者Skave
相关产品推荐
相关产品推荐

