TS2322:输入“ Foo |不可将“酒吧”分配给“ Foo&Bar”类型

时间:2020-04-22 18:43:52

标签: typescript

我正在尝试将一个项目存储在一个对象中,其中对象键是类型的区分符。

请参见下面的修改。

这是一个简单的例子:

type Foo = {
  id: 'foo'
}

type Bar = {
  id: 'bar'
}

type Container = {
  foo: Foo
  bar: Bar
};

const container: Container = {
  foo: { id: 'foo' },
  bar: { id: 'bar' },
};

function storeValue(value: Foo | Bar) {
  container[value.id] = value; // <= Error happens here. See below.
}

这是我得到的完整错误

TS2322: Type 'Foo | Bar' is not assignable to type 'Foo & Bar'.
   Type 'Foo' is not assignable to type 'Foo & Bar'.
     Type 'Foo' is not assignable to type 'Bar'.
       Types of property 'id' are incompatible.
         Type '"foo"' is not assignable to type '"bar"'.

我尝试过这样的事情:

type Container = {
  [key in (Foo|Bar)['id']]: FooBar | undefined
}

有了这个,错误就消失了……但是随后它允许这样的事情(在container.foo中分配一个Bar):

function storeValue(value: Foo | Bar) {
  container.foo = value; // OK... but it shouldn't be.
}

是否可以从key推断类型?

type Container = {
  [key in (Foo|Bar)['id']]: ??? | undefined // <= Here, infer the type based on the `key`
}

我阅读了文档,FAQ,尝试了很多事情,阅读了很多SO帖子,GitHub问题...我什么都没找到。

编辑:其他示例(仍进行了简化,但更接近于我的用例。仅供参考,我正在使用Twilio Video)

type DataPublication = {
  kind: 'data';
  // other props
}

type AudioPublication = {
  kind: 'audio';
  // other props
}

type VideoPublication = {
  kind: 'video';
  // other props
}

type Publication = DataPublication | AudioPublication | VideoPublication;

class Whatever {
  publications: {
    data: DataPublication | undefined
    audio: AudioPublication | undefined
    video: VideoPublication | undefined
  } = {
    data: undefined,
    audio: undefined,
    video: undefined
  }

  handlePublishedWorking(publication: Publication) {
    switch (publication.kind) {
      case 'data':
        this.publications.data = publication; // publication is narrowed to DataPublication
        break;
      case 'audio':
        this.publications.audio = publication; // publication is narrowed to AudioPublication
        break;
      case 'video':
        this.publications.video = publication; // publication is narrowed to VideoPublication
        break;
    }
  }

  handlePublishedNotWorking(publication: Publication) {
    this.publications[publication.kind] = publication;
  }
}

1 个答案:

答案 0 :(得分:1)

问题是您试图依靠运行时信息来进行区分。

TypeScript在运行时不存在。在运行时只有JavaScript。

function storeValue(value: Foo | Bar) {
  container[value.id] = value;
}

TypeScript在这一行中唯一了解value.id的信息是它可以是fooboo

它无法评估值并采用正确的类型,因为它可以更改。

因此,container[value.id]的类型为container['foo'] | container['boo'], 因此您看到的错误。

您需要告诉TypeScript它是哪种类型。

一种方法是通过控制流:

type Foo = {
  id: 'foo'
}

type Bar = {
  id: 'bar'
}

type Container = {
  foo: Foo
  bar: Bar
};

const container: Container = {
  foo: { id: 'foo' },
  bar: { id: 'bar' },
};

function storeValue(value: Foo | Bar) {
  if (value.id === 'foo')
    container['foo'] = value;
  else
    container['bar'] = value;
}

您会发现else的TypeScript可以正确地分析出只有bar是可能的值,从而执行正确的类型检查。