为何指定函数参数时TypeScript泛型D被推断为unknown?
TypeScript泛型推断问题:linkedSignal中D被推断为unknown的原因与优化
问题重现
原始类型定义
declare function linkedSignal<S, D>(options: { source: () => S; computation: (source: NoInfer<S>, previous?: {source: NoInfer<S>; value: NoInfer<D>}) => D; }): D;
使用示例
linkedSignal({ source: () => 3, computation: (source, previous) => { return 3; }, })
上述代码中,即使computation明确返回数值类型3,但只要声明了第二个参数previous,泛型D就会被推断为unknown。
原因分析
TypeScript的泛型推断存在优先级和依赖关系:
- 当
computation包含previous参数时,TypeScript会优先尝试从previous的类型结构推断D,但previous.value使用了NoInfer<D>,这直接阻止了TypeScript从computation的返回值反向推导D的路径。 - 由于
previous是可选参数,调用时没有提供具体的类型实例,TypeScript无法从previous获取D的有效类型信息,最终只能将D推断为默认的unknown。 - 泛型
D同时作为computation的返回值类型和previous.value的类型,形成了循环依赖的推断链路,NoInfer的使用彻底打破了原本可以通过返回值推断的逻辑。
优化方案
调整类型定义结构,让TypeScript优先从computation的返回值推断D,再将该类型应用到previous.value上。核心是移除previous.value上的NoInfer<D>,并确保推断顺序合理:
declare function linkedSignal<S, D>(options: { source: () => S; computation: (source: NoInfer<S>, previous?: { source: NoInfer<S>; value: D }) => D; }): D;
优化原理
- 移除
previous.value的NoInfer<D>后,TypeScript会优先从computation的返回值推断D,再将已确定的D类型应用到previous.value上,避免了循环推断冲突。 - 保留
NoInfer<S>确保S仅从source函数推断,不受computation参数类型的干扰,维持原有设计意图。
如果需要更明确地拆分推断逻辑,也可以将options拆分为交叉类型,进一步强化推断顺序:
declare function linkedSignal<S, D>( options: { source: () => S } & { computation: (source: NoInfer<S>, previous?: { source: NoInfer<S>; value: D }) => D; } ): D;
内容的提问来源于stack exchange,提问作者Matthieu Riegler
相关产品推荐
相关产品推荐

