将 union 转换为 map

Transform union into a map

让我们从这个给定的类型开始:

type Union =
  { type: 'A', a: string } |
  { type: 'B', b: number }

我希望我们在这里结束:

type MappedUnion = {
  A: { type: 'A', a: string }
  B: { type: 'B', b: number }
}

这是一些伪代码:

type MappedUnion<item in Union> = {[ i: item['type'] ]: item}

我想你可以做到以下几点?尚未详尽测试,但似乎有效。

type Union =
  { type: 'A', a: string } |
  { type: 'B', b: number }

type MappedUnion = {[P in Union['type']]: Union & {type: P}};

const v: MappedUnion = {
  'A': {type: 'A', a: "hello"},
  'B': {type: 'B', b: 3},
}

遗憾的是,不太管用;如果我这样做

type MappedUnionTarget = {
  A: { type: 'A', a: string }
  B: { type: 'B', b: number }
}

function cvt1(x: MappedUnion): MappedUnionTarget {
  return x;
}

然后我得到 Type '({ type: "A"; a: string; } & { type: "A"; }) | ({ type: "B"; b: number; } & { type: "A"; })' is not assignable to type '{ type: "A"; a: string; }'. 我认为打字稿可以得出结论 ({ type: "B"; b: number; } & { type: "A"; })never 相同。


如果您愿意使用 Typescript 2.8(在撰写本文时尚未发布,如果您从 TypeScript 存储库 git pull 可以使用,并且支持条件运算符 ? :在类型级别)那么你似乎可以做到

type FilterType<T, K> = (T extends { type: K } ? T : never);

type MappedUnion = {[P in Union['type']]: FilterType<Union, P>};

因为 FilterType<T, K> 是我希望 T & {type: K} 表现的样子。