Typescript:区分条件类型中的泛型联合

标签 typescript typescript-generics typescript-types

采用以下代码片段:

type A = {a: unknown}
type TakesA<T extends A> = unknown

type B = { b: unknown }
type TakesB<T extends B> = unknown

type AB = A | B
type TakesAB<T extends AB> = T extends A ? TakesA<T> : TakesB<T>

我收到错误类型“T”不满足约束“B”。类型“AB”不可分配给类型“B”。但是,条件类型不应该在 else 子句中将 T 的类型缩小为 B 吗?为什么会发生这种行为?我应该如何解决它?

最佳答案

这是同一问题的另一个(更简单的)示例:

TS Playground

type Example<T extends string | number> = T extends number
  ? T['toFixed']
  : T['length']; /*
    ~~~~~~~~~~~
Type '"length"' cannot be used to index type 'T'.(2536) */

我们期望 TypeScript 在 false/else 分支中将 T 缩小为 string,但这并没有发生(从 TS 版本 4.9.3 开始)至少)。


这在 GitHub 问题 microsoft/TypeScript#29188 中进行了解释。 :

jack-williams commented on Jan 3, 2019:


Conditional types do not produce substitution types for the false branch of the conditional (a.k.a do not narrow in the false branch).

There was an attempted fix here: #24821, however this was closed.

...

weswigham commented on Jan 3, 2019:


@jack-williams had identified the core of the issue. We use "substitution" types internally to track the constraints applied to a type within the true branch of a conditional, however we do no such tracking for the false branch. This means that you can't actually bisect a union type with a conditional right now, as @lodo1995 points out, you must chain two conditions and invert the check so your logic is in the true branch instead.

Part of the reason why we didn't move forward with #29011 (other than one of the relations I identified not holding up under scrutiny) is that tracking falsified constraints with substitution types kinda works... but when you perform the substitution, the information is lost, since we do not currently have a concept of a "negated" type (I mitigated this a little bit by remateriaizling the substitutions that tracked negative constraints late, but that's a bit of a hack). We cannot say that the given T extends string ? "ok" : T that the type of T in the false branch is a T & ~string , for example - we do not have the appropriate type constructors currently.

We regularly bring up how we really do probably need it for completeness, but the complexity "negated" types bring is... large? At least that's what we seem to think - it's not immediately obvious that a ~string is an alias for "any type except those which are or extend string", and therefore that a ~string & ~number is "any type except strings or numbers" (note how despite the use of &, the english you read as used the word "or").

So we're very aware of what needs to be done to make this work better... we're just having trouble convincing ourselves that it's "worth it".


所以 - 总而言之 - the advice来自Dimava是合理的,您应该使用这种类型,并为最终的“无法访问”分支提供无条件的 never 结果:

type TakesAB<T extends AB> = T extends A
  ? TakesA<T>
  : T extends B
    ? TakesB<T>
    : never;

TS Playground

关于Typescript:区分条件类型中的泛型联合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74575042/

相关文章:

typescript - 输入gapi.savetoandroidpay

typescript :如何组织应用程序

html - 有什么方法可以使用 Angular Material 中的 mat-table 来编辑表格的特定列

TypeScript:返回与参数相同的类型

typescript - 'CustomEnum.Case' 可分配给类型 'T' 的约束,但 'T' 可以使用约束 'CustomEnum' 的不同子类型进行实例化

使用静态方法的 typescript 继承

unit-testing - Jasmine 路线 spy - 未定义不是一个对象(评估 'navigate.calls.mostRecent().args' )

javascript - typescript + Angular : variable changing context

typescript - 基于Typescript接口(interface)中另一个字段值的条件字段

TypeScript 和 React Native : Are the type definitions for RN styles wrong?