如何在TypeScript中为映射对象的不同方法动态类型化参数?
解决TypeScript动态映射函数参数的类型错误问题
我在父包装函数里尝试把函数的预期参数动态映射到选中的函数时遇到了问题。已经成功映射了所有可调用方法,但TypeScript对传入的参数抛出错误。
以下是我的代码:
type TGenerateSetA = { city: number; address: number; } const setASelects = { city: ({ appendComma }: { appendComma: boolean }) => `city${appendComma ? ',' : ''}`, address: ({ appendComma }: { appendComma: boolean }) => `address${appendComma ? ',' : ''}`, }; type TGenerateSetB = { user: number; role: number; } const setBSelects = { user: ({ appendComma }: { appendComma: boolean }) => `user${appendComma ? ',' : ''}`, role: ({ appendComma }: { appendComma: boolean }) => `role${appendComma ? ',' : ''}`, }; type TSetObject = Record<string, ({ appendComma }: { appendComma: boolean }) => string>; interface ISetMapping { SET_A: TSetObject SET_B: TSetObject } const SELECTS: ISetMapping = { SET_A: setASelects, SET_B: setBSelects, } type TGenerateCommonResult = { selectFrom: keyof ISetMapping; } & TGenerateSetA const generateCommonResult = ({ selectFrom, ...args }: TGenerateCommonResult) => { const result: string[] = []; const totalEnabledSelects = Object.values(args).reduce((acc, shouldSelect) => acc + shouldSelect, 0); let selectsAdded = 0; Object.entries(args).forEach(([select, shouldSelect], index, self) => { if (shouldSelect) { result.push(SELECTS[selectFrom][select]({appendComma: selectsAdded + 1 !== totalEnabledSelects})); } selectsAdded++; }); return result.join(' '); } console.log(generateCommonResult({ selectFrom: 'SET_A', city: 1, address: 0 })); // 预期无错误 console.log(generateCommonResult({ selectFrom: 'SET_B', user: 1, city: 0 })); // 预期报错 console.log(generateCommonResult({ selectFrom: 'SET_B', user: 1, role: 0 })); // 预期无错误
核心问题
当前TGenerateCommonResult类型只固定关联了TGenerateSetA,导致TypeScript无法根据selectFrom的值动态约束参数结构,所以传入SET_B时还能传city这类不属于它的参数。必须用泛型建立selectFrom和参数结构的强关联。
修改方案
- 先修正
ISetMapping的类型定义,让TypeScript能精准识别每个集合的键值对:
// 直接用typeof推导,保留每个集合的精确类型 type ISetMapping = { SET_A: typeof setASelects; SET_B: typeof setBSelects; };
- 将
generateCommonResult改为泛型函数,让selectFrom的类型决定参数结构:
// 泛型T绑定selectFrom的类型,动态生成对应的参数结构 type TGenerateCommonResult<T extends keyof ISetMapping> = { selectFrom: T; } & { [K in keyof ISetMapping[T]]: number }; const generateCommonResult = <T extends keyof ISetMapping>({ selectFrom, ...args }: TGenerateCommonResult<T>) => { const result: string[] = []; const totalEnabledSelects = Object.values(args).reduce((acc, shouldSelect) => acc + shouldSelect, 0); let selectsAdded = 0; // 类型断言告诉TypeScript:args的键就是对应集合的方法名 (Object.entries(args) as [keyof ISetMapping[T], number][]).forEach(([select, shouldSelect]) => { if (shouldSelect) { result.push(SELECTS[selectFrom][select]({ appendComma: selectsAdded + 1 !== totalEnabledSelects })); } selectsAdded++; }); return result.join(' '); };
效果验证
修改后,调用generateCommonResult({ selectFrom: 'SET_B', user: 1, city: 0 })会直接触发TypeScript错误,符合预期;而合法的调用(比如SET_A传city/address、SET_B传user/role)则不会报错,同时代码逻辑保持不变。
内容的提问来源于stack exchange,提问作者Mike K
相关产品推荐
相关产品推荐

