为什么';t Typescript允许使用整个优化类型吗?

为什么';t Typescript允许使用整个优化类型吗?,typescript,Typescript,我希望以下截取的代码可以编译,但事实并非如此: function f1(x: "A") {} function f2(x: {member: "A"}) {} let x: {member: "A" | "B"} = {member: "A"} if (x.member == "A") { f1(x.member) // compiles f2(x) // doesn't compile } 我想知道这是为什么 请注意,我正在使用strict选项进行编译 我的使用案例是,我想

我希望以下截取的代码可以编译,但事实并非如此:

function f1(x: "A") {}
function f2(x: {member: "A"}) {}

let x: {member: "A" | "B"} = {member: "A"}
if (x.member == "A") {
    f1(x.member) // compiles
    f2(x) // doesn't compile
}
我想知道这是为什么

请注意,我正在使用
strict
选项进行编译

我的使用案例是,我想为不可变类型编写一个类型安全更新程序函数,我发现它比我目前看到的更符合人体工程学

type Update1 = <Value, 
    K1 extends keyof Value>
    (value: Value, k1: K1, replacement: Value[K1]) => Value;

type Update2 = <Value, 
    K1 extends keyof Value, 
    K2 extends keyof Value[K1]>
    (value: Value, k1: K1, k2: K2, replacement: Value[K1][K2]) => Value;

type Update3 = <Value,
    K1 extends keyof Value,
    K2 extends keyof Value[K1],
    K3 extends keyof Value[K1][K2]>
    (value: Value, k1: K1, k2: K2, k3: K3, replacement: Value[K1][K2][K3]) => Value;

// ...

type Update = Update1 & Update2 & Update3; // ...

let update: Update = (...args: Array<any>) => {
    throw "to be implemented..."
};
但当涉及到狭窄的类型时,它不起作用:

if (data.value2.kind == "A") {
    data.value2.aValue // compiles, can access aValues
    data = update(data, "value2", "aValue", 42) // doesn't compile, not expected!
}
我认为它不起作用,因为typescript没有对类型参数
使用缩小的类型。当我手动提供它时,我不能像在第一个示例中那样为它分配
数据
,我不能用
x
调用
f2

  • 这种行为是故意的吗?若有,原因为何
  • 是虫子吗
  • 有解决办法吗

    • 您在这里使用的功能是。从描述此功能的页面:

      判别属性类型保护是一个表达式,其形式为x.p==v,x.p==v,x.p!=v、 或x.p!==v、 其中p和v是字符串文字类型或字符串文字类型的并集的属性和表达式。鉴别属性类型保护将x的类型缩小为具有鉴别属性p和可能值v之一的x的组成类型

      因此,在您的第一个示例中,问题是
      x
      不是联合类型,因此没有可缩小
      x
      范围的成分。然而,这将如预期的那样起作用:

      let x: {member: "A" } | {member: "B"} = {member: "A"}
      if (x.member == "A") {
          f1(x.member) // compiles
          f2(x) // works now !
      }
      
      将此逻辑应用到更复杂的示例中,数据必须是一个联合体,以便有可能缩小到联合体的一个组成部分,因此人们可能认为这是可行的:

      interface DataA {
          value1: number
          value2: {
              kind: "A", 
              aValue: number, 
              abValue: number
          }
      }
      interface DataB {
          value1: number
          value2: {
              kind: "B", 
              bValue: number, 
              abValue: number
          }
      }
      
      let data: DataA | DataB = undefined as any // dummy value
      if (data.value2.kind == "A") {
          data.value2.aValue // compiles, can access aValues
          data = update(data, "value2", "aValue", 42) // doesn't compile, not expected!
      }
      
      然而,上面的代码不起作用,关于嵌套的歧视联合,这个开放的代码建议扩展类型缩小的工作方式,以涵盖这一点

      唯一的解决办法是使用自定义类型的防护装置:

      function isDataA(data: DataA | DataB) : data is DataA {
          return data.value2.kind == "A";
      }
      if (isDataA(data)) {
          data.value2.aValue // compiles, can access aValues
          data = update(data, "value2", "aValue", 42) // compiles
      }
      

      您可能还对以下哪种解释感兴趣:
      {member:“A”|“B”}
      {member:“A”}{member:“B”}
      是的,看起来我在缩小问题范围时做错了什么。
      function isDataA(data: DataA | DataB) : data is DataA {
          return data.value2.kind == "A";
      }
      if (isDataA(data)) {
          data.value2.aValue // compiles, can access aValues
          data = update(data, "value2", "aValue", 42) // compiles
      }