TypeScript中如何将对象属性转为函数的类型匹配固定参数?
这在TypeScript中完全可行,核心是通过元组类型约束返回函数的参数,同时在运行时将参数组装成目标对象传递给原函数。下面提供两种可靠的实现方案:
方案一:显式指定属性顺序(最可靠)
通过让useCommand接收属性名的顺序数组,既保证类型层面的参数顺序/数量/类型校验,又在运行时明确知道如何组装对象:
import { discard } from "../utilities/utility" export function useCommand< TObject extends {}, TKeys extends readonly (keyof TObject)[], TValue = void >( keys: TKeys, fn: (arg: TObject) => Promise<TValue>, onError: (e: unknown) => void = (e) => discard(e), onSuccess: (ret: TValue) => void = (r) => discard(r) ) { return async (...args: { [I in keyof TKeys]: TObject[TKeys[I]] }) => { const argObj = {} as TObject; keys.forEach((key, index) => { argObj[key] = args[index]; }); try { const result = await fn(argObj); onSuccess(result); } catch (err) { onError(err); } }; }
使用示例:
// 显式传入属性顺序数组,TypeScript会自动推导参数类型 const func = useCommand(['email', 'code'], async (arg: {email: string, code: number}) => { // 业务逻辑 }); func("JohnDoe@example.com", 1234); // ✅ 类型、顺序、数量全部符合要求 func("JohnDoe@example.com"); // ❌ 报错:参数数量不足 func(1234, "JohnDoe@example.com"); // ❌ 报错:类型顺序不匹配
方案二:依赖对象字面量属性顺序(更简洁)
利用TypeScript对对象字面量类型的属性顺序保留特性,以及ES6+中对象属性的有序性,自动推导参数元组:
import { discard } from "../utilities/utility" // 辅助类型:提取对象属性名的有序元组 type TupleKeys<T> = T extends object ? (keyof T extends infer K ? K extends string ? [K, ...TupleKeys<Omit<T, K>>] : [] : []) : []; // 辅助类型:提取对象属性值的有序元组 type TupleValues<T> = TupleKeys<T> extends infer Ks ? Ks extends Array<keyof T> ? { [I in keyof Ks]: T[Ks[I]] } : never : never; export function useCommand<TObject extends {}, TValue = void>( fn: (arg: TObject) => Promise<TValue>, onError: (e: unknown) => void = (e) => discard(e), onSuccess: (ret: TValue) => void = (r) => discard(r) ) { // 运行时获取属性顺序(依赖对象字面量的属性顺序) const argKeys = Object.keys({} as TObject) as TupleKeys<TObject>; return async (...args: TupleValues<TObject>) => { const argObj = {} as TObject; argKeys.forEach((key, index) => { argObj[key] = args[index]; }); try { const result = await fn(argObj); onSuccess(result); } catch (err) { onError(err); } }; }
使用示例:
// 直接传入接收对象的函数,自动推导参数类型和顺序 const func = useCommand(async (arg: {email: string, code: number}) => { // 业务逻辑 }); func("JohnDoe@example.com", 1234); // ✅ 合法 func("JohnDoe@example.com"); // ❌ 报错:缺少第二个参数 func(1234, "JohnDoe@example.com"); // ❌ 报错:类型不匹配
关键说明
- 两种方案都解决了原代码的问题:返回函数的参数会严格校验类型、数量、顺序,不符合要求的调用会在编译阶段报错。
- 方案一通过显式传入属性顺序,避免了对TS/JS属性顺序的依赖,适合需要严格控制参数顺序的场景。
- 方案二更简洁,但依赖TS对对象字面量类型的属性顺序保留(这是TS 2.1+的稳定特性),以及ES6+中对象字符串属性的有序性,适合大多数常规场景。
- 原代码中的
if (!!arguments)已被移除,因为箭头函数中不存在arguments对象,且剩余参数args已经可以正确捕获所有传入的参数。
内容的提问来源于stack exchange,提问作者Simple Fellow
相关产品推荐
相关产品推荐

