Defining a variable as one variant of a discriminated union in TypeScript

你说的曾经没有我的故事 提交于 2020-01-06 02:22:05

问题


I have the following typescript code which uses a discriminated union to distinguish between some similar objects:

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}
}

In the function buildBeast it accepts a string that complies with all possible types of my Beast type, yet it does not allow me to declare the myBeast as type Beast due to this error:

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"'.

It seems that all cases still yield a correct Beast yet TS seems to have trouble coercing the different types. Any ideas?


回答1:


TypeScript doesn't do control flow analysis by walking through union types and making sure that each type works. It would be nice if it did so or if you could tell it to do so, and in fact I've made a suggestion to that effect, but it isn't currently possible.

For now, the only way to deal with it that I know of are the workarounds I mention in that suggestion: either do a type assertion (which is unsafe) or walk the compiler through the different cases (which is redundant). Here are the two different ways:

Assertion:

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

Walk compiler through different cases:

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

Hey, if you think that TypeScript should allow you to distribute control flow analysis over union types, maybe head over to that suggestion and give it a 👍 or describe your use case. Or, if those above solutions work for you, that's great too.

Hope that helps. Good luck!



来源:https://stackoverflow.com/questions/51064527/defining-a-variable-as-one-variant-of-a-discriminated-union-in-typescript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!