如何创建仅允许必填参数的通用TypeScript函数类型?
如何创建无可选参数的通用函数工具类型?
我正在尝试实现一个工具类型,用来确保函数没有可选参数。目前针对特定函数签名的实现是有效的,但不知道怎么扩展成支持任意数量必填参数的通用类型。
当前实现
type NoOptionalArgs<F> = F extends (...args: infer P) => any ? undefined extends P[number] ? never : F : F; // 测试结果符合预期: type fn0 = NoOptionalArgs<() => any>; // OK - 无参数 type fn1 = NoOptionalArgs<(x: string) => any>; // OK - 单个必填参数 type fn2 = NoOptionalArgs<(x?: string) => any>; // never - 包含可选参数
但当我尝试创建通用类型时出现问题:
type fnType = NoOptionalArgs<(...args: any[]) => any>; // 结果为never
需求
我想要的fnType需要满足:
- 允许包含任意数量的必填参数
- 禁止包含任何可选参数
- 支持任意参数类型
示例
const good: fnType = (x: string) => 'x'; // 合法,无报错 const bad: fnType = (x?: string) => 'x'; // 应当报错
这是否可行?
其他无效尝试
尝试1
type RequiredArray<T> = T extends any[] ? undefined extends T[number] ? never : T : never; type fnType = <T extends any[]>(...args: T extends RequiredArray<T> ? T : never) => any;
尝试2
type fnType = <T extends readonly any[]>( ...args: { [K in keyof T]: Exclude<T[K], undefined> } ) => any;
解决方案
要实现这个需求,我们可以利用TypeScript对函数参数元组的结构推断,通过约束参数元组中不存在可选标记来达成目标。
基础实现版本
type NoOptionalParams<F> = F extends (...args: infer P) => infer R ? P extends [...{ [K in keyof P]: Exclude<P[K], undefined> }] ? F : never : never; // 通用函数类型定义 type fnType = NoOptionalParams<(...args: any[]) => any>;
验证效果
const good1: fnType = () => 'ok'; // 合法 const good2: fnType = (a: number, b: string) => true; // 合法 const bad1: fnType = (x?: string) => 'x'; // 报错:类型不匹配 // 注意:必填的可空参数(如string | undefined)不属于可选参数,会被允许 const validNullable: fnType = (x: string | undefined) => 'x'; // 合法
原理说明
- 通过
infer P推断函数的参数元组P - 检查原元组
P是否等于「将每个成员排除undefined后的元组」:如果原元组包含可选参数,排除undefined后的元组会丢失可选标记,结构与原元组不一致,因此条件不成立,返回never - 对于
(...args: any[]) => any这个通用签名,该约束会自动过滤掉所有带可选参数的函数,仅保留参数全为必填的函数
严格区分可选参数与必填可空参数的版本
如果需要严格拒绝可选参数,但允许必填的可空参数(即区分x?: string和x: string | undefined),可以利用元组的可选标记特性实现:
type IsAllRequired<T extends any[]> = T extends [...infer U, infer Last] ? IsAllRequired<U> & (Last extends undefined ? never : unknown) : unknown; type NoOptionalParamsStrict<F> = F extends (...args: infer P) => infer R ? IsAllRequired<P> extends unknown ? F : never : never; type fnTypeStrict = NoOptionalParamsStrict<(...args: any[]) => any>;
验证效果
const validNullable: fnTypeStrict = (x: string | undefined) => 'x'; // 合法 const invalidOptional: fnTypeStrict = (x?: string) => 'x'; // 报错:类型不匹配
内容的提问来源于stack exchange,提问作者Anon A. Mouse
相关产品推荐
相关产品推荐

