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
相关产品推荐
相关产品推荐

