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

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"); // ❌ 报错:类型不匹配

关键说明

  1. 两种方案都解决了原代码的问题:返回函数的参数会严格校验类型、数量、顺序,不符合要求的调用会在编译阶段报错。
  2. 方案一通过显式传入属性顺序,避免了对TS/JS属性顺序的依赖,适合需要严格控制参数顺序的场景。
  3. 方案二更简洁,但依赖TS对对象字面量类型的属性顺序保留(这是TS 2.1+的稳定特性),以及ES6+中对象字符串属性的有序性,适合大多数常规场景。
  4. 原代码中的if (!!arguments)已被移除,因为箭头函数中不存在arguments对象,且剩余参数args已经可以正确捕获所有传入的参数。

内容的提问来源于stack exchange,提问作者Simple Fellow

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 01:37:03