我有一个泛型类型,它允许对象中的任一属性为undefined,但不能同时为undefined:

type RequireOne<T> =
  | {
      a: NonNullable<T>
      b: NonNullable<T>
    }
  | {
      a: NonNullable<T>
      b: undefined
    }
  | {
      a: undefined
      b: NonNullable<T>
    }

现在,如果我在声明函数时使用此类型,它可以很好地处理具体类型:

const test1 = ({ a, b }: RequireOne<string>): string => a ?? b

但是,如果我也try 使相同的函数泛型,则会失败:

const test2 = <T>({ a, b }: RequireOne<T>): T => a ?? b 
// ERROR: Type 'NonNullable<T> | undefined' is not assignable to type 'T'.

这是Typescript 的预期行为吗?如果是,为什么?

推荐答案

一般来说,genericscontrol flow analysis在一起打得不好.在这个特定的例子中,看起来您遇到了microsoft/TypeScript#50652中报告的问题,其中union不被视为discriminated union,而预期的判别式属性是通用的.

在这里,您试图通过判断a属性的nullishness来区分RequireOne<T>,但类型判断器不会这样做.

既然有问题,那么行为至少是known,但这并不意味着necessarily是"预期的",因为问题是Backlog,并被标记为"需要调查"(截至今天,2023/08/04).因此,这个问题可能会在一段时间内得不到解决,如果有的话.

但是,总的来说,Tyescrip确实不能对泛型类型执行任意的高阶分析.NonNullable<T>等于as of TypeScript 4.8,等于intersection typeT & {}.对于任何specific类型T,编译器将理解T & {}undefined是可区分的.但它不能将这些东西抽象为"对于所有类型TT & {}undefined是可区分的"的一般语句.除非有人将其硬编码到编译器中,否则您将遇到这个问题.

Typescript相关问答推荐

TypeScript类型不匹配:在返回类型中处理已经声明的Promise Wrapper

在TypeScript中,除了映射类型之外还使用的`in`二进制运算符?

使Typescribe强制所有对象具有相同的 struct ,从两个选项

无法从Chrome清除还原的初始状态数据

被推断为原始类型的交集的扩充类型的交集

类型脚本满足将布尔类型转换为文字.这正常吗?

分解对象时出现打字错误

如何按属性或数字数组汇总对象数组

自定义 Select 组件的类型问题:使用带逗号的泛型类型<;T与不带逗号的<;T&>;时出错

TypeScrip:使用Union ToInterval辅助对象,但保留子表达式

如何通过属性名在两个泛型数组中找到匹配的对象?

如何为特定参数定义子路由?

为什么看起来相似的代码在打字时会产生不同的错误?

Select 类型的子项

定义一个只允许来自另一个类型的特定字符串文字的类型的最佳方式

数据库中的表请求返回如下日期:2023-07-20T21:39:00.000Z 我需要将此数据格式化为 dd/MM/yyyy HH:mm

React-Router V6 中的递归好友路由

如何确定剧作家中给定定位器的值?

带有 Select 器和映射器的Typescript 泛型

是否可以从 TypeScript 的类型推断中排除this?