尝试更新标记的联合时出现令人惊讶的类型错误

时间:2019-02-05 11:55:37

标签: typescript

我希望下面的代码能正常工作,但第7行错误并显示消息Type '"one"' is not assignable to type '"two"'.

type One = { type: 'one', value: number }
type Two = { type: 'two', value: number }

type OneOrTwo = One | Two

function handleOneOrTwo(oneOrTwo: OneOrTwo): OneOrTwo {
  const nextOneOrTwo: OneOrTwo = {
    type: oneOrTwo.type,
    value: oneOrTwo.value + 1,
  }
  return nextOneOrTwo
}

这真是令人惊讶。特别是考虑到当我更新该功能以使用switch语句(具有相同功能)时,它可以正常工作:

type One = { type: 'one', value: number }
type Two = { type: 'two', value: number }

type OneOrTwo = One | Two

function handleOneOrTwo(oneOrTwo: OneOrTwo): OneOrTwo {
  const nextOneOrTwo = {
    value: oneOrTwo.value + 1,
  }

  switch (oneOrTwo.type) {
    case 'one':
      return { ...nextOneOrTwo, type: oneOrTwo.type }
    case 'two':
      return { ...nextOneOrTwo, type: oneOrTwo.type }
    default:
      throw unreachable(oneOrTwo)
  }
}

function unreachable(value: never) {
  return new Error(value)
}

即使强制转换类型也不起作用(type: oneOrTwo.type as 'one' | 'two')。

我已经阅读了Type Error when trying to extract type from unions的答案,但仍不确定为什么会发生此错误以及如何解决它。

1 个答案:

答案 0 :(得分:4)

出现错误的原因是,当您访问联合的type时是one | two。但是,当您分配对象文字时,检查的工作方式是,如果对象文字至少与联合的一个成员匹配,则该分配有效。

所以让我们看看。对象文字的类型为{ type : 'one' | 'two' value: number }

对象文字类型是否与One兼容?否,type在对象文字中为'one' | 'two',但在'one'中为One

对象文字类型是否与Two兼容?否,type在对象文字中为'one' | 'two',但在'two'中为Two

因此,您最终得到的对象常量可以分配给联合的任何一个成员。

非类型断言选项是使用扩展表达式,它将保留原始的联合类型:

function handleOneOrTwo(oneOrTwo: OneOrTwo): OneOrTwo {
  const nextOneOrTwo: OneOrTwo = {
     ...oneOrTwo,
    value: oneOrTwo.value + 1,
  }
  return nextOneOrTwo
}

或者使用类型断言:

function handleOneOrTwo(oneOrTwo: OneOrTwo): OneOrTwo {
   const nextOneOrTwo: OneOrTwo = {
     type: oneOrTwo.type,
     value: oneOrTwo.value + 1,
   } as OneOrTwo
   return nextOneOrTwo
}