TypeScript传对象时类型推断过泛问题(无需显式标注泛型)
TypeScript 对象参数函数的泛型类型推断问题
将三参数函数改为接收单个对象参数时,遇到了意外的泛型类型推断失效问题。这个问题源于对Solid.js(或React)组件封装逻辑的泛化尝试。
基础类型定义
Result 判别联合类型与类型谓词
type ResultOk<T> = { status: "ok", data: T }; type ResultErr<E> = { status: "error", error: E }; type Result<T,E> = ResultOk<T> | ResultErr<E>; const isOk = <T,E>(result: Result<T, E>): result is ResultOk<T> => { return result.status === "ok"; } const isErr = <T,E>(result: Result<T, E>): result is ResultErr<E> => { // 注:原代码存在逻辑错误,此处修正为判断"error"状态 return result.status === "error"; }
Predicate 类型定义
type Predicate<S extends T, T> = (e:T) => e is S;
三参数版本(正常工作)
函数实现
// Version 1: 传递三个独立参数 const applyWhen_params = <S extends T, T, R>( t: T, pred: Predicate<S, T>, fn: (s: S) => R ): R|null => { if(pred(t)) { return fn(t); } else { return null; } }
使用示例(无类型错误)
function example_params(x: Result<number, string>) { applyWhen_params( x, isOk, (a) => { return a.data + 1; } ) }
此时泛型参数能被正确推断:S为ResultOk<number>,T为Result<number, string>,R为number。
对象参数版本(类型推断失效)
函数实现
// Version 2: 传递单个对象参数 const applyWhen_props = <S extends T, T, R>(props: { t: T, pred: Predicate<S, T>, fn: (s: S) => R }): R|null => { if(props.pred(props.t)) { return props.fn(props.t); } else { return null; } }
使用示例(类型错误)
function example_props(x: Result<number, string>) { applyWhen_props({ t: x, pred: isOk, fn: (a) => { return a.data + 1; } // ^^^^ // 类型错误:Property 'data' does not exist on type 'Result<number, string>'. }) }
错误的推断结果
此时TypeScript推断的泛型参数过于宽泛:
S被推断为Result<number, string>(与T完全相同)fn的参数a类型被推断为Result<number, string>,而非预期的ResultOk<number>
解决方案(无需显式标注泛型)
问题根源在于对象参数模式下,TypeScript无法自动关联pred的类型谓词返回值与fn的参数类型。我们可以通过重构泛型定义,从pred的返回类型中提取出目标子类型S,让类型推断逻辑更明确:
修正后的函数实现
// 从类型谓词中提取被断言的目标子类型 type PredicateTarget<P> = P extends (e: any) => e is infer S ? S : never; const applyWhen_props = <T, P extends (e: T) => e is any, R>(props: { t: T, pred: P, fn: (s: PredicateTarget<P>) => R }): R | null => { if (props.pred(props.t)) { return props.fn(props.t as PredicateTarget<P>); } else { return null; } }
修正后的使用示例
function example_props(x: Result<number, string>) { applyWhen_props({ t: x, pred: isOk, fn: (a) => { return a.data + 1; } // 现在a被正确推断为ResultOk<number> }) }
原理说明
通过PredicateTarget类型从pred的返回类型中提取出被断言的子类型S,让TypeScript能够根据传入的isOk自动推导出S为ResultOk<number>,从而正确约束fn的参数类型。
内容的提问来源于stack exchange,提问作者Benjamin Bray
相关产品推荐
相关产品推荐

