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

TypeScript:如何通过泛型函数识别含默认值的可选/必填参数?

TypeScript中根据函数参数默认值自动推断必填/可选参数

当前你的代码中,所有转换器参数都被定义为Partial<Record<keyof P, number>>,导致所有参数均为可选类型,无法区分是否带有默认值。要实现让无默认值的参数为必填、带默认值的为可选,且调用时缺少必填参数会报错的需求,可通过调整类型定义,让TypeScript能捕获转换器函数的参数结构,同时限制参数键仅来自params的定义。

修改后的实现代码

type UnitType = "distance" | "area";

type ConverterParams = Record<string, UnitType>;

// 定义转换器函数类型:参数键必须来自P的键,值为number
type ConverterFn<P extends ConverterParams> = (
  params: { [K in keyof P]?: number } & { [K: string]: never } // 禁止传入params外的额外键
) => number;

type Converters<P extends ConverterParams> = Record<string, ConverterFn<P>>;

type UnitConversion<P extends ConverterParams, C extends Converters<P>> = {
  readonly params: P;
  readonly converters: C;
};

// 调整构建器泛型,确保转换器参数合规并保留默认值推断
const buildConverters = <
  P extends ConverterParams,
  C extends { [K in keyof C]: ConverterFn<P> }
>(
  { params, converters }: { params: P; converters: C }
): UnitConversion<P, C> => {
  return { params, converters };
};

测试实例与效果

const testConverter = buildConverters({
  params: {
    width: "distance",
    height: "distance",
  },
  converters: {
    // width无默认值 → 推断为必填;height有默认值 → 推断为可选
    surface: ({ width, height = 1 }) => (width || 0) * height,
  },
});

// ❌ 报错:缺少必填参数width
testConverter.converters.surface({});

// ✅ 合法:仅传入必填的width
testConverter.converters.surface({ width: 5 });

// ✅ 合法:传入width和可选的height
testConverter.converters.surface({ width: 5, height: 3 });

// ❌ 报错:传入了params中未定义的键
testConverter.converters.surface({ width: 5, depth: 2 });

额外优化:严格限制必填参数范围

如果需要强制转换器的必填参数必须是params中存在的键,可进一步优化类型定义,避免出现未定义的参数:

// 提取函数的必填参数键
type RequiredParams<T extends (...args: any[]) => any> = Exclude<
  keyof Parameters<T>[0],
  undefined
>;

// 验证转换器函数的必填参数是否都属于P的键
type ValidConverterFn<P extends ConverterParams, T extends (...args: any[]) => any> = 
  T extends (params: infer Args) => number 
  ? RequiredParams<T> extends keyof P 
    ? Args extends { [K in keyof P]?: number } 
      ? T 
      : never 
    : never 
  : never;

type Converters<P extends ConverterParams> = Record<string, ValidConverterFn<P, any>>;

const buildConverters = <
  P extends ConverterParams,
  C extends { [K in keyof C]: ValidConverterFn<P, C[K]> }
>(
  { params, converters }: { params: P; converters: C }
): UnitConversion<P, C> => {
  return { params, converters };
};

这个版本会在定义转换器时就报错,比如如果转换器中使用了params未定义的键作为必填参数,TypeScript会直接提示类型错误。

内容的提问来源于stack exchange,提问作者Yanick Rochon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 07:25:18