如果T 没有重复的属性值类型,那么您希望RecToDU<T> 成为其所有属性值类型的union;否则你希望它是never。 T 的所有属性值类型的并集可以简单地通过 indexing 计算成 T 和 the union of its keys: T[keyof T]
(see this Q/A)
.
因此,您将希望 RecToDU<T> 成为 conditional type 形式的 type RecToDU<T> = XXX extends YYY ? T[keyof T] : never 或 type RecToDU<T> = XXX extends YYY ? never : T[keyof T]。那么我们要为XXX 和YYY 做些什么呢?
这是一种方法:
type RecToDU<T> = unknown extends {
[K in keyof T]-?: T[K] extends Omit<T, K>[Exclude<keyof T, K>] ? unknown : never
}[keyof T] ? never : T[keyof T]
让我们来看看这一点:
{ [K in keyof T]-?: T[K] extends Omit<T, K>[Exclude<keyof T, K>] ? unknown : never }
我们正在做的是mapping 在T 的键中的每个属性键K,并且对于每个属性值T[K],我们将其与Omit<T, K>[Exclude<keyof T, K>] 进行比较。 The Omit<T, K> utility type 生成一个看起来像 T 但删除了 K 属性的类型;并且the Exclude<X, K> utility type 生成一个类型,该类型从X 中的任何联合成员中过滤掉K。所以Omit<T, K>[Exclude<keyof T, K>]是T中所有属性值类型的联合除了键K处的属性。
例如,如果T 是{a: 0, b: 1, c: 2} 并且K 是"a",那么T[K] 是0。 Omit<T, K> 是 {b: 1, c: 2},Exclude<keyof T, K> 是 "b" | "c",所以 Omit<T, K>[Exclude<keyof T, K>] 是 1 | 2。所以在T[K] extends Omit<T, K>[Exclude<keyof T, K>] ? ... 中,我们正在比较0 extends 1 | 2 ? ... 。这是错误的,但如果 c 的值类型为 0 而不是 2,则会变为正确。
所以如果T[K] extends Omit<T, K>[Exclude<keyof T, K>],这意味着K 的属性值在其他一些属性中也是重复的。否则,这意味着K 处的属性值是唯一的。请注意,该条件类型的其余部分是? unknown : never,这意味着对于重复的属性,我们产生the unknown type(吸收联合中所有其他类型的“顶级类型”),对于独特的属性,我们产生the never type( “底部类型”被吸收到联合中的所有其他类型)。同样,T 是 {a: 0, b: 1, c: 2},我们将产生 {a: never, b: never, c: never},但对于 T 是 {a: 0, b: 1, c: 0},我们将产生 {a: unknown, b: never, c: unknown}。
现在我们来看看
{ [K in keyof T]-?:
T[K] extends Omit<T, K>[Exclude<keyof T, K>] ? unknown : never
}[keyof T]
这与以前相同,但我们使用keyof T 对其进行索引。因此,我们采用映射类型并获取其值的并集。对于{a: 0, b: 1, c: 2},这是never | never | never,也就是never。但是对于{a: 0, b: 1, c: 0},这是unknown | never | unknown,即unknown。由于unknown 吸收了联合中的所有其他类型,而never 吸收了联合中的所有其他类型,因此never 的唯一方法是每个属性都是唯一的。如果甚至一个属性值是重复的(呃,我想至少必须有两个,也许?也许不是,如果其中一个是另一个的超类型......呃,没关系),那么@987654401 @出来了。
所以我们有一个条件类型,如果任何属性重复,则计算为 unknown,如果它们都是唯一的,则计算为 never。因此:
type RecToDU<T> = unknown extends {
[K in keyof T]-?: T[K] extends Omit<T, K>[Exclude<keyof T, K>] ? unknown : never
}[keyof T] ? never : T[keyof T]
因为unknown extends XXX 仅在XXX 本身为unknown 时才为真,因此此检查仅在T 具有重复属性时为真,如果T 具有所有唯一属性则为假。对于重复的属性,我们返回never,对于唯一的属性,我们返回T[keyof T]。
哇,让我们看看它是否有效:
const VALUES = {
field1: "fieldA",
field2: "fieldB",
field3: "fieldC"
} as const
type VALUESLiterals = RecToDU<typeof VALUES>
// type VALUESLiterals = "fieldA" | "fieldB" | "fieldC"
好的,然后:
const VALUES = {
field1: "fieldA",
field2: "fieldB",
field3: "fieldA"
} as const
type VALUESLiterals = RecToDU<typeof VALUES>
// type VALUESLiterals = never
看起来不错!
请注意,RecToDU<T> 的上述实现仅使用包含非optional 属性且没有index signatures 且其属性值为单个literal types 而非其他类型的联合或交集的对象类型进行了测试。如果这些条件被改变,那么上面实现的RecToDu<T> 可能会产生一些奇怪或不受欢迎的结果。因此,请注意针对您关心的用例进行测试,并在必要时相应地更改定义。
Playground link to code