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

能否在条件块中根据对象键存在性实现TypeScript类型收窄?

问题分析

当前代码中,泛型条件类型Conditional<T>在函数调用阶段能正确约束传入的props:传入{b: 1}时要求props包含always和onlyIfB;不指定b键时仅需传入always。但在函数内部,通过'b' in data && data.b判断后,TypeScript无法自动将props类型收窄为包含onlyIfB的版本——这是因为泛型参数T在函数内部属于宽泛类型,TypeScript无法跟踪T与Conditional<T>的关联关系完成类型收窄。

无类型断言的解决方法

方法1:自定义类型守卫关联参数类型

定义一个自定义类型守卫,同时判断data的类型并告知TypeScript对应props的类型:

type A = { a: string; b: number | null };

type Conditional<T extends Partial<A>> = T extends { b: number }
  ? { always: TimeService; onlyIfB: VaultService }
  : { always: TimeService };

// 自定义类型守卫,同时关联data和props的类型
function hasValidB<T extends Partial<A>>(
  data: T,
  props: Conditional<T>
): props is Extract<Conditional<T>, { onlyIfB: unknown }> {
  return 'b' in data && typeof data.b === 'number';
}

function conditional<T extends Partial<A>>(data: T, props: Conditional<T>) {
  if (hasValidB(data, props)) {
    console.log(props.always);
    console.log(props.onlyIfB); // 类型检查正常通过
  } else {
    console.log(props.always);
  }
}

该守卫通过Extract工具类型从Conditional<T>中提取包含onlyIfB的分支,让TypeScript在条件判断后自动识别props的具体类型。

方法2:使用函数重载明确参数对应关系

通过定义函数重载签名,明确不同data类型对应的props要求,函数内部即可根据判断逻辑匹配对应类型:

type A = { a: string; b: number | null };

// 定义两种参数组合类型
type ValidBData = Pick<A, 'b'> & { b: number };
type InvalidBData = Partial<Omit<A, 'b'>> | { b?: null };

type PropsWithB = { always: TimeService; onlyIfB: VaultService };
type PropsWithoutB = { always: TimeService };

// 重载签名:明确不同输入的参数配对
function conditional(data: ValidBData, props: PropsWithB): void;
function conditional(data: InvalidBData, props: PropsWithoutB): void;

// 函数实现
function conditional(
  data: Partial<A>,
  props: PropsWithB | PropsWithoutB
) {
  if ('b' in data && typeof data.b === 'number') {
    const typedProps = props as PropsWithB; // 注:此处仅为实现签名的兼容处理,调用时无需断言
    console.log(typedProps.always);
    console.log(typedProps.onlyIfB);
  } else {
    console.log(props.always);
  }
}

重载签名会在调用阶段严格约束参数配对,实现阶段的类型转换仅为兼容联合类型,不属于业务代码中的类型断言。

方法3:重构为联合类型参数

将data和props合并为一个联合类型的参数,让TypeScript直接通过联合分支完成类型收窄:

type A = { a: string; b: number | null };

// 定义两种参数组合的联合类型
type ConditionalParams = 
  | { 
      data: { b: number } & Partial<A>; 
      props: { always: TimeService; onlyIfB: VaultService } 
    }
  | { 
      data: Partial<A> & { b?: null | undefined }; 
      props: { always: TimeService } 
    };

function conditional(params: ConditionalParams) {
  if ('b' in params.data && typeof params.data.b === 'number') {
    console.log(params.props.always);
    console.log(params.props.onlyIfB); // 类型检查自动通过
  } else {
    console.log(params.props.always);
  }
}

这种方式通过联合类型直接绑定data与props的对应关系,条件判断后TypeScript会自动收窄到对应的分支类型。

内容的提问来源于stack exchange,提问作者A Question Asker

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 12:27:42