【问题标题】:Negating typescript types?否定打字稿类型?
【发布时间】:2018-08-11 03:28:27
【问题描述】:

我想在 typescript 中创建一个简单的 NOT 运算符,您可以在其中将所有原语组合到某个类型 A 的联合中,这些联合不是第二种类型 B 的联合的原始成员。这可以使用条件类型来完成。例如,如果您有类型:

type A = 'a' | 'b' | 'c';
type B = 'c' | 'd' | 'e'; 

...然后我想将它们映射到第三个派生类型 [A - B],在这种情况下,将产生:

type C = 'a' | 'b'

这似乎可以使用如下所示形式的条件来实现。但是,我完全不明白为什么下面的 NOT 运算符似乎给了我想要的东西,但明确地拼出完全相同的条件逻辑却没有:

type not_A_B_1 = A extends B ? never : A;   // 'a' | 'b' | 'c'

type Not<T, U> = T extends U ? never : T;   
type not_A_B_2 = Not<A, B>                  // 'a' | 'b'

here

如果我在这里遗漏了一些 TS 微妙之处,可以告诉我,这可以解释为什么 not_A_B_1not_A_B_2 不等价吗?谢谢。

【问题讨论】:

    标签: typescript negation


    【解决方案1】:

    你遇到了distributive conditional types

    检查类型是裸类型参数的条件类型称为分布式条件类型。分布式条件类型在实例化期间自动分布在联合类型上。例如,T extends U ? X : Y 的实例化具有 A | B | C 的类型参数 T 被解析为 (A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)

    所以在这个:

    type not_A_B_1 = A extends B ? never : A;   // 'a' | 'b' | 'c'
    

    A 是具体类型,而不是类型参数,因此条件类型不会分布在其组成部分中。

    但是在

    type Not<T, U> = T extends U ? never : T;   
    

    T 是一个裸类型参数,所以条件类型 确实 被分发。 “裸体”是什么意思?这意味着T,而不是T 的某些类型函数。所以在{foo: T} extends W ? X : Y中,类型参数T是“clothed”,所以不会分发。

    这导致了一种在您不想要分布式条件类型时关闭它们的方法:给类型参数加上衣服。最简单且最简洁的方法是使用一个元素的tuple:所以,

    T extends U ? V : W // naked T
    [T] extends [U] ? V : W // clothed T
    

    由于[T] extends [U] 应该在T extends U 时为真,所以除了分布性之外,它们是等价的。因此,让我们将Not&lt;&gt; 更改为不可分配:

    type NotNoDistribute<T, U> = [T] extends [U] ? never : T;   
    type not_A_B_2 = NotNoDistribute<A, B>                  // 'a' | 'b' | 'c'
    

    现在not_A_B_2not_A_B_1 相同。如果您更喜欢原来的 not_A_B_2 行为,那么请使用分布条件类型,例如 Not&lt;&gt;。如果您更喜欢非分配行为,请使用具体类型或覆盖类型参数。这有意义吗?

    顺便说一下,您的Not&lt;T,U&gt; 类型已经作为predefined type 在标准库中以Exclude&lt;T,U&gt; 的形式出现,意思是“从T 中排除那些可分配给U 的类型”。

    希望对您有所帮助。祝你好运!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-05-07
      • 2017-02-20
      • 2021-11-26
      • 1970-01-01
      • 2015-11-06
      • 2018-01-16
      • 1970-01-01
      相关资源
      最近更新 更多