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

如何让TypeScript将对象的type属性作为判别式实现类型收窄?

为什么TypeScript无法根据type属性收窄泛型对象的类型?

问题描述

在以下TypeScript代码中,当判断obj.type === "Bar"时,TypeScript无法将obj.relatedObject的类型收窄为ObjTwo,报错提示属性three不存在于ObjOne | ObjTwo类型上:

type Types = "Foo" | "Bar";

interface ObjOne {
  one: "one";
  two: "two";
}

interface ObjTwo {
  three: "three";
  four: "four";
}

type ExtractRelatedObject<T extends Types> = T extends "Foo"
  ? ObjOne
  : T extends "Bar"
    ? ObjTwo
    : never;

interface Baz<T extends Types> {
  type: T;
  relatedObject: ExtractRelatedObject<T>;
}

function foo<T extends Types>(obj: Baz<T>) {
  if (obj.type === "Bar") {
    obj.relatedObject.three;
    //^?? Property 'three' does not exist on type 'ObjOne | ObjTwo'.
  }
}

原因分析

这是因为泛型参数T是一个单一的、未具体化的类型变量,而不是联合类型。当你在foo函数中使用Baz<T>时,TypeScript无法将T拆解为联合类型的成员来进行分布式条件类型的匹配。即使T的约束是Types(即"Foo" | "Bar"),但在函数内部,T被视为一个可能是"Foo"、"Bar"或整个联合类型的单一类型,此时ExtractRelatedObject<T>会被解析为ObjOne | ObjTwo,而非根据type属性动态关联的具体类型。

简言之:泛型函数中的类型参数是“整体”处理的,不会自动展开为联合类型进行分布式收窄。

修复方法及验证

有效修复:使用联合类型替代泛型参数

将函数参数类型改为Baz<"Foo"> | Baz<"Bar">,此时TypeScript会将联合类型的每个成员单独处理,通过type属性的判断可以正确收窄relatedObject的类型:

function fooFix1(obj: Baz<"Foo"> | Baz<"Bar">) {
  if (obj.type === "Bar") {
    obj.relatedObject.three;
                     // ^?? 正确识别为ObjTwo.three: "three"
  }
}

这种写法的本质是让TypeScript直接处理已具体化的联合类型,而非未绑定的泛型变量,此时类型收窄可以正常工作。

无效尝试的原因分析

为泛型参数添加默认类型Types的写法无效,因为默认类型只是在调用函数未指定T时的 fallback,函数内部的T仍然是一个单一的类型变量,不会自动展开为联合类型:

function fooBroken1<T extends Types = Types>(obj: Baz<T>) {
  if (obj.type === "Bar") {
    obj.relatedObject.three;
    //^?? 仍然报错:Property 'three' does not exist on type 'ObjOne | ObjTwo'.
  }
}

这里的T依然是一个泛型变量,TypeScript无法确定它是"Foo"、"Bar"还是整个联合类型,因此无法进行精准的类型收窄。


内容的提问来源于stack exchange,提问作者Magnus

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 07:44:55