You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 22:47:14