在TypeScript中将变量定义为已区分联合的一种变体

时间:2018-06-27 13:49:32

标签: typescript discriminated-union

我有以下打字稿代码,该代码使用区分的并集来区分某些相似的对象:

interface Fish  {
  type: 'FISH',
}

interface Bird  {
  type: 'BIRD',
  flyingSpeed: number,
}

interface Ant  {
  type: 'ANT',
}

type Beast = Fish | Bird | Ant

function buildBeast(animal: 'FISH' | 'BIRD' | 'ANT') {
    const myBeast: Beast = animal === 'BIRD' ? {
        type: animal,
        flyingSpeed: 10
    } : {type: animal}
}

在函数buildBeast中,它接受一个符合我的types类型的所有可能的Beast的字符串,但不允许我将myBeast声明为类型Beast由于此错误:

Type '{ type: "BIRD"; flyingSpeed: number; } | { type: "FISH" | "ANT"; }' is not assignable to type 'Beast'.
  Type '{ type: "FISH" | "ANT"; }' is not assignable to type 'Beast'.
    Type '{ type: "FISH" | "ANT"; }' is not assignable to type 'Ant'.
      Types of property 'type' are incompatible.
        Type '"FISH" | "ANT"' is not assignable to type '"ANT"'.
          Type '"FISH"' is not assignable to type '"ANT"'.

似乎所有情况下仍然都能产生正确的Beast,但TS似乎难以强制使用不同的类型。有什么想法吗?

1 个答案:

答案 0 :(得分:2)

TypeScript不会通过遍历联合类型并确保每种类型都起作用来control flow analysis。如果这样做了,或者您可以告诉它这样做,那会很好,并且实际上我已经made a suggestion了,但是目前不可能。

就目前而言,据我所知,解决该问题的唯一方法是我在该建议中提到的解决方法:要么进行类型断言(这是不安全的),要么使编译器经历不同的情况(这是多余的)。这是两种不同的方式:

断言:

function buildBeast(animal: 'FISH' | 'BIRD' | 'ANT') {
  const myBeast: Beast = animal === 'BIRD' ? {
    type: animal,
    flyingSpeed: 10
  } : {type: animal} as Fish | Ant;
}

通过不同情况介绍编译器:

function buildBeast(animal: 'FISH' | 'BIRD' | 'ANT') {
  const myBeast: Beast = animal === 'BIRD' ? {
    type: animal,
    flyingSpeed: 10
  } : (animal === 'FISH') ? { 
    type: animal 
  } : { type: animal };
}

嘿,如果您认为TypeScript应该允许您将控制流分析分布在并集类型上,则可以直接参考该建议并给出建议或描述您的用例。或者,如果上述解决方案对您有用,那也很好。

希望有帮助。祝你好运!