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

如何在TypeScript中强制泛型函数仅接受恰好一个参数(复杂类型定义场景)

如何在TypeScript中强制泛型函数仅接受恰好一个参数(复杂类型定义场景)

嗨,我完全懂你现在的困扰——你想给自定义npm包写一个装饰器生成函数,要求传入的被装饰函数必须恰好接受一个参数,而且要在开启noImplicitAny的JS/TS项目里正常工作。之前的类型定义已经能约束参数和返回值的类型,但就是管不住参数的数量,比如无参或者多参的函数还能通过类型检查,这确实挺头疼的。

问题根源分析

你之前定义的SupplierFunction里有两个重载:一个是单参数签名,另一个是剩余参数签名((...arg0: ...)),这就给了TypeScript“钻空子”的机会,无参或多参函数也能匹配到这个类型。咱们得把类型约束改得更严格,直接限定传入的函数必须是只接受单个参数的函数。

解决方案代码

先调整类型定义,新增一个工具类型来精准判断函数参数数量,再修改装饰器的泛型约束:

// 工具类型:严格校验函数是否恰好接受一个参数
type ExactlyOneArgFn<T> = T extends (arg: infer P) => infer R 
  ? unknown[] extends P[] 
    ? never // 排除无参函数(无参时infer P会得到unknown)
    : P[] extends [unknown] 
      ? (arg: P) => R // 仅保留恰好一个参数的函数类型
      : never // 排除多参函数
  : never;

// 保留你原来的Constraints类型,用于过滤特殊类型
type Constraints<C> = C extends
| Function
| PropertyDescriptor
| Document
| Omit<RegExp, "lastIndex">
| void
| never
? never
: C;

// 重新定义装饰器,泛型直接约束为恰好单参数的函数
export default function decorateFunction<F extends ExactlyOneArgFn<F>>(fn: F) {
  return async (payload: Parameters<F>[0]) => {
    const result: ReturnType<F> = await new Promise((resolve) => {
      resolve(fn(payload));
    });
    // 对返回值应用Constraints约束
    return result as Constraints<ReturnType<F>>;
  };
}

代码逻辑解释

  • ExactlyOneArgFn工具类型的核心逻辑:
    1. 先判断目标类型是不是函数,提取它的参数类型P和返回值类型R
    2. 如果是无参函数(P为unknown),直接返回never排除
    3. 如果是多参函数(P的数组形式不是[unknown]),同样返回never排除
    4. 只有恰好单参数的函数会保留原类型

测试案例验证

现在你的测试案例会得到预期的类型检查结果:

应该通过的案例(无类型错误)

// Should OK
const f1 = decorateFunction((payload) => {
  return 2;
});

const f2 = decorateFunction((payload: string) => {
  return false;
});

const f3 = decorateFunction(({ payload }) => {
  return (1 + 2).toString();
});

const f4 = decorateFunction(({ payload }: { payload: boolean }) => {
  return null;
});

应该报错的案例(现在会正确触发类型错误)

// Should Error
const e1 = decorateFunction(); // 错误:缺少必填的函数参数
const e2 = decorateFunction(() => {}); // 错误:无参函数不符合约束
const e3 = decorateFunction(""); // 错误:传入的不是函数类型
const e4 = decorateFunction(() => true); // 错误:无参函数不符合约束
const e6 = decorateFunction((payload1, payload2) => false); // 错误:多参函数不符合约束

复用函数的场景(依然保持原有类型校验)

// Should OK
const r1 = f1("anything"); // Promise<number>
const r2 = f2("string"); // Promise<boolean>
const r3 = f3({ payload: "anything" }); // Promise<string>
const r4 = f4({ payload: true }); // Promise<null>

// Should Error
const re1 = f1("any").then((e) => e.charAt(1)); // 错误:number类型没有charAt方法
const re2 = f2(1); // 错误:参数类型应为string
const re3 = f3("parameter should be object"); // 错误:参数应为包含payload的对象
const re4 = f4({ nothing: "property invalid" }); // 错误:缺少必填的payload属性

备注:内容来源于stack exchange,提问作者Azvya Erstevan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.22 13:34:30