打字稿根据对象值更改类型结构

Typescript changing type structure depending on object value

我有一个 API returns 一个大数据数组,它是对象的混合体,其中数据结构根据类型而变化,我不明白我应该如何创建一个好的类型结构为了它。 我相信我做对了,但是当我尝试检查它是哪种类型时,我收到错误 属性 X does not exist on type.

type EchoGroup = {
  group: {
    img: { name: string; type: "ECHO" };
    specialData: { meta: string; copy: string }[];
  };
  owner: boolean;
};
type CharlieGroup = {
  group: {
    img: { name: string; type: "CHARLIE" };
    specialData: { meta: string; usage: string; price: number }[];
  };
  owner: boolean;
};

const sample: (EchoGroup | CharlieGroup)[] = [
  {
    group: {
      img: { name: "Test 1", type: "CHARLIE" },
      specialData: [
        { meta: "string", usage: "private", price: 10 },
        { meta: "string", usage: "public", price: 20 },
      ],
    },
    owner: false,
  },
  {
    group: {
      img: { name: "Test 2", type: "ECHO" },
      specialData: [{ meta: "string", copy: "private" }],
    },
    owner: true,
  },
];
sample.map((single: EchoGroup | CharlieGroup) => {
  switch (single.group.img.type) {
    case "ECHO":
      console.log(single.group.specialData[0].copy);
      // Error:
      // Property 'copy' does not exist on type '{ meta: string; copy: string; }[] | { meta: string; usage: string; price: number; }[]'.
      // Property 'copy' does not exist on type '{ meta: string; copy: string; }[]'.
      break;
    case "CHARLIE":
      console.log(single.group.specialData[0].price);
      break;
  }
});

问题是打字稿的类型缩小 doesn't narrow type 父对象取决于子对象的判别值:

type A = { type: "a", a: number }
type B = { type: "b", b: number }

type X = { type: A, a: string }
type Y = { type: B, b: string }

declare let x: X | Y

if (x.type.type === "a") {
    x.a // Type Error
    x.type.a // no error. `x.type` is narrowed to A
}

playground link

A discriminant property only applies to the object it's directly a member of. So in your example, inside the if block, you can access x.type.a (but not x.type.b), but there are no effects on the containing object x.

有一个 PR 可以提供这种功能。但它仍在进行中。

截至目前,在任何结构内缩小类型的唯一方法是将判别式 属性 保持在相同或更高级别:

type EchoGroup = {
  group: {
    type: 'ECHO',
    img: { name: string };
    specialData: { meta: string; copy: string }[];
  };
  owner: boolean;
};
type CharlieGroup = {
  group: {
    type: 'CHARLIE',
    img: { name: string };
    specialData: { meta: string; usage: string; price: number }[];
  };
  owner: boolean;
};
...
sample.map((single: EchoGroup | CharlieGroup) => {
  switch (single.group.type) {
    case "ECHO":
      console.log(single.group.specialData[0].copy); // works as expected
      break;
    case "CHARLIE":
      console.log(single.group.specialData[0].price); // works too
      break;
  }
});

playground link