合并两个联合类型并创建对象打字稿

问题描述 投票:0回答:2

我刚刚从

javascript
转到
typescript
,我正处于学习阶段。我有两种联合类型,我想从中创建一种类型。

我尝试了一切,

Exclude and Extract
也输入但没有运气。

下面是我的代码,包含预期结果和实际结果。

type Union1 = 'key1' | 'key2';

type Union2 = 'value1' | 'value2';

type MergeObj<K extends string | number, V> = {
  [T in V as K]: T;
};

type Result = MergeObj<Union1, Union2>;

// expected: { key1: 'value1', key2: 'value2' }
// actual: { key1: 'value1' | 'value2', key2: 'value1' | 'value2' }

打字稿: https://www.typescriptlang.org/play?#code/C4TwDgpgBAqgdgSwPZwIxQLxQOQGsIirZQA+O+IATNgNwBQdoksiKlmOAbgIYA2ArhCKkufQdXqNw0ALIQATgHMIAeQBGAKwA8AaSgQAHsAhwAJgGco54PIR xFIuPwC2ahQBooANQB8HAN50UFAA2gAqUHbeUNyWOgC6AFxQYfQAvpJM0ABKEOb8vMAcckqqmlrwyGielW w+kgD0DfoGkADGxqbJ-lAUqMnYPAJC2J4ULAND4sRpdE0xHfx83b0E-ALDwmSDYhDUYwQTG4Jbx3szQA

编辑1

在实际情况中,我有两个混合类型的联合,如下

type Union1 = 'key1' | 'key2' | 'key3' | 4 | 'key5';

type Union2 = 'value1' | 'value2' | 3 | 'value4' | 'value5';

因此联合可以具有

string
number
并且联合将具有相同的价值始终。 IE。目前在示例中,我有 5 个。

所以我想从这两种联合类型转换为如下一种类型。

type Result = {
  key1: 'value1';
  key2: 'value2';
  key3: 3;
  4: 'value4';
  key5: 'value5';
}
typescript types
2个回答
2
投票

这本质上是不可能的,出于同样的原因,不可能将联合类型转换为元组类型(请参阅如何将联合类型转换为元组类型):TypeScript中的联合类型是无序的(或者更准确地说, ,他们的顺序是一个实现细节,不能依赖)。 类型

type U = 1 | 2
type U = 2 | 1
完全等效,编译器可以自由地将其中一种更改为另一种。 因此,通过“索引”或“位置”或“顺序”关联两个并集是不可能的,因为您无法区分
type Keys = "key1" | "key2"
type Keys = "key2" | "key1"
之间的区别。

如果你有键和值的元组,你可以迭代它们来获取你想要的对象。 事实上,我们可以通过梳理编译器确定的联合顺序的动作来实现您的

MergeObj
类型,如下所示:

// get the "Last" element in a union
type Last<T> =
  ((T extends any ? ((x: (() => T)) => void) : never) extends
    ((x: infer I) => void) ? I : never) extends () => infer U ? U : never

// convert a union to a tuple
type UnionToTuple<T, A extends any[] = []> =
  [T] extends [never] ? A : UnionToTuple<Exclude<T, Last<T>>, [Last<T>, ...A]>

// merge key/value unions by first turning them to tuples
type MergeObj<K extends PropertyKey, V> =
  [UnionToTuple<K>, UnionToTuple<V>] extends [infer KK extends PropertyKey[], infer VV extends any[]] ?
  { [P in keyof KK & keyof VV & `${number}` as KK[P]]: VV[P] } : never

我不会详细说明它是如何工作的,因为这不是重点。 我们来尝试一下:

// type Nums = 1 | 2 | 3 | 4 | 5; // 😇    

type Union1 = 'key1' | 'key2' | 'key3' | 4 | 'key5';
type Union2 = 'value1' | 'value2' | 3 | 'value4' | 'value5';

type O = MergeObj<Union1, Union2>
/* type O = {
    key1: "value1";
    key2: "value2";
    key3: 3;
    4: "value4";
    key5: "value5";
} */

嘿,这完全符合你的要求,对吧? 那么问题出在哪里呢? 好吧,如果你看一下注释掉的行

// type Nums = 1 | 2 | 3 | 4 | 5; // 😇
如果我们取消注释该行,就会发生一些事情:

type Nums = 1 | 2 | 3 | 4 | 5; // 😈    

type Union1 = 'key1' | 'key2' | 'key3' | 4 | 'key5';
type Union2 = 'value1' | 'value2' | 3 | 'value4' | 'value5';

type O = MergeObj<Union1, Union2>
/* type O = {
    4: 3;
    key1: "value1";
    key2: "value2";
    key3: "value4";
    key5: "value5";
} */

生成的对象类型已发生更改,这仅仅是由于看似不相关的代码的更改。 编译器可以自由地以它喜欢的方式和顺序来表示联合。 事实证明,通过在代码中比定义

1 | 2 | 3 | 4 | 5
Union1
之前更早地显式引用
Union2
,编译器将倾向于在遇到的任何未来联合中首先列出这些:

// type Union1 = 4 | "key1" | "key2" | "key3" | "key5"
// type Union2 = 3 | "value1" | "value2" | "value4" | "value5"

这会毁了一切。 在这里继续下去没有什么意义。


回顾一下:根本没有办法将

Union1
Union2
定义为并集,然后根据“索引”、“位置”或“顺序”将它们可靠地关联起来。 您应该找到一种完全不同的方法,例如在元组等有序数据结构中记录所需的顺序;或者从所需的对象开始并从中导出并集;或完全放弃,具体取决于您的用例。

Playground 代码链接


0
投票
export type colorsType = 'primary' | 'info' | 'secondary' | 'warning' | 'danger' | 'light' | 'dark'
export type btnColorsType =
  | colorsType
  | 'outline-primary'
  | 'outline-info'
  | 'outline-secondary'
  | 'outline-warning'
  | 'outline-danger'
  | 'outline-light'
  | 'outline-dark'

新类型 btnColorsType 将具有前 2 个类型的全部值👍

© www.soinside.com 2019 - 2024. All rights reserved.