繁体   English   中英

打字稿:基于类型的可区分联合

[英]Typescript: Discriminated union based on type

为什么以下对于 TS 是不可能的? 为什么我不能使用类型作为判别式?

export interface A1 {
  plop: number;
}
export interface B1 {
  hop: number;
}

export interface A {
  foo: number;
  bar: string;
  inner: A1;
}

export interface B {
  foo: number;
  bar: string;
  inner: B1;
}

export type AorB = A | B;

function test(): AorB {
  let inner: A1 | B1;

  if (Math.random()) {
    inner = {plop: 4};
  } else {
    inner = {hop: 43};
  }

  return {
    foo: 42,
    bar: 'plop',
    inner
  };
}

TS 编译器告诉我:

  Type '{ foo: number; bar: string; inner: A1 | B1; }' is not assignable to type 'B'.
    Types of property 'inner' are incompatible.
      Type 'A1 | B1' is not assignable to type 'B1'.
        Property 'hop' is missing in type 'A1' but required in type 'B1'.

考虑一下你的类型的扩展。 AorB类型表示联合:

{
  foo: number;
  bar: string;
  inner: A1;
} | {
  foo: number;
  bar: string;
  inner: B1;
}

请注意, A1 | B1类型在此没有任何地方。 A1 | B1出现。 也就是说,类型AorB期望有一个对象,其中inner属性已知并固定为A1B1

可是等等? 由于封闭对象的属性是相同的(即foobar ),所以上面的类型不应该等同于:

{
  foo: number;
  bar: string;
  inner: A1 | B1;
}

从逻辑上讲,这是有道理的,您可以将内部联合分布在封闭对象类型上,并看到您将获得与AorB相同的对象联合。 事实上,这看起来像是一个已知问题,但目前 TypeScript 无法做出这种推断。


为了解决这个问题,我看到了一些选项。 首先,您可以只为AorB使用单个接口,其中inner属性的类型为A1 | B1 A1 | B1

interface AorB {
  foo: number;
  bar: string;
  inner: A1 | B1;
}

作为另一种选择,您可以修改在test函数中构造返回对象的方式,以向打字稿清楚地表明结果对象具有固定的inner属性:

function test2(): AorB {
  let outer = {
    foo: 42,
    bar: 'plop',
  };

  if (Math.random()) {
    // Clearly has type A
    return {
      ...outer,
      inner: {plop: 4}
    }
  } else {
    // Clearly has type B
    return {
      ...outer,
      inner: {hop: 43}
    }
  }
}

这样,对于 TS 编译器来说,很明显一个分支返回类型A和另一个类型B ,它们完全匹配AorB的返回类型。

最后,因为我们人类可以看到这两种类型实际上是等价的,所以你总是可以忽略这个错误:

function test(): AorB {
  let inner: A1 | B1;

  if (Math.random()) {
    inner = {plop: 4};
  } else {
    inner = {hop: 43};
  }

  // @ts-ignore: This is equivalent to the type AorB after distributing the inner union...
  return {
    foo: 42,
    bar: 'plop',
    inner
  };
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM