如何让TypeScript优先针对第一个泛型参数的不匹配报错?
TypeScript验证框架的泛型参数优先级问题
问题背景
我在TypeScript中实现了一个验证框架,核心逻辑是仅当校验器验证输入通过后,才会调用对应的处理器函数,核心代码如下:
interface RawEvent { untrustedInput: string; } interface ValidatedEvent<T> { data: T; }; type Response = void; type RawHandler = (event: RawEvent) => Response; type ValidatedHandler<T> = (event: ValidatedEvent<T>) => Response; type ValidatorCallback<T> = (input: unknown) => input is T; export function wrapHandler<T>( handler: ValidatedHandler<T>, validator: ValidatorCallback<T> ): RawHandler { return (event: RawEvent) => { const data: unknown = JSON.parse(event.untrustedInput); const dataIsValid = validator(data); if (dataIsValid) { return handler({data}); } }; }
遇到的具体问题
1. 泛型不匹配时错误提示不符合预期
当处理器与校验器的泛型类型冲突时,比如下面的代码:
export const handler = wrapHandler( (evt: ValidatedEvent<number>) => {}, (d): d is string => typeof d === "string" );
我期望TypeScript提示**第一个参数(处理器期望number,但应匹配校验器的string)**存在问题,但实际报错指向第二个参数。
2. 无法设置默认的“无数据”校验器
尝试给校验器添加默认值时,代码报错:
export function wrapHandler<T>( handler: ValidatedHandler<T>, validator?: ValidatorCallback<T> = (_): _ is void => true ): RawHandler
错误提示为:'T' could be instantiated with an arbitrary type which could be unrelated to 'void',而非限制只能传入ValidatedHandler<void>类型的处理器。
核心疑问
是否可以控制多个泛型参数的推导优先级?让TypeScript优先根据处理器的类型推导T,再校验校验器是否匹配;或者根据需求调整推导顺序?
解决方案
1. 用函数重载强制推导优先级
通过函数重载,明确让TypeScript优先从处理器参数推导T,同时限制默认校验器的使用场景:
// 重载1:带自定义校验器,优先从handler推导T export function wrapHandler<T>(handler: ValidatedHandler<T>, validator: ValidatorCallback<T>): RawHandler; // 重载2:无校验器时,仅允许handler为ValidatedHandler<void> export function wrapHandler(handler: ValidatedHandler<void>): RawHandler; // 核心实现 export function wrapHandler<T>( handler: ValidatedHandler<T>, validator?: ValidatorCallback<T> ): RawHandler { const defaultValidator: ValidatorCallback<void> = (_): _ is void => true; const usedValidator = validator ?? (defaultValidator as ValidatorCallback<T>); return (event: RawEvent) => { const data: unknown = JSON.parse(event.untrustedInput); const dataIsValid = usedValidator(data); if (dataIsValid) { return handler({data} as ValidatedEvent<T>); } }; }
该方案的效果:
- 当处理器与校验器泛型不匹配时,错误会指向校验器不符合处理器的
T类型(符合预期的提示方向) - 不传校验器时,必须传入
ValidatedHandler<void>类型的处理器,否则直接报错
2. 用条件类型约束默认值
如果不想使用重载,也可以通过条件类型限制默认校验器的生效范围:
export function wrapHandler<T>( handler: ValidatedHandler<T>, validator?: T extends void ? ValidatorCallback<void> : ValidatorCallback<T> ): RawHandler { const defaultValidator: ValidatorCallback<void> = (_): _ is void => true; const usedValidator = validator ?? (defaultValidator as ValidatorCallback<T>); return (event: RawEvent) => { const data: unknown = JSON.parse(event.untrustedInput); const dataIsValid = usedValidator(data); if (dataIsValid) { return handler({data} as ValidatedEvent<T>); } }; }
这种方式同样能实现:不传校验器时,处理器必须为ValidatedHandler<void>;传校验器时,优先从处理器推导T,校验器必须匹配该类型。
问题根源解释
原代码中,TypeScript会同时从处理器和校验器推导T,当两者类型冲突时,它会尝试寻找兼容类型或直接报错在第二个参数。通过重载或条件类型,我们强制TypeScript优先从处理器推导T,再校验校验器的兼容性,从而改变了错误提示的方向。
内容的提问来源于stack exchange,提问作者natevw
相关产品推荐
相关产品推荐

