I want to build a type from type reverser as explained here Record Type Reverser
const VALUE = {
field1: "fieldA",
field2: "fieldB",
field3: "fieldC"
} as const
type UReversed = UniqueReverser<typeof VALUE>
/*
should yield
{
fieldA: "field1"
fieldB: "field2"
fieldC: "field3"
}
*/
However this should only work if all literal values are unique!
Given the code below
const VALUE = {
field1: "fieldA",
field2: "fieldB",
field3: "fieldA" // "fieldA" as a duplicate value here
} as const
type UReversed = UniqueReverser<typeof VALUE>
// should yield never
I want the type definition to yield yieldnever
Probably this could help Ensure the Uniqueness of Literal Values in a Record Type
CodePudding user response:
In this answer I explain how to detect if an object type has duplicate property value types (subject to caveats about optional properties, index signatures, unions, intersections, subtypes, and other complications). I won't duplicate this part of the answer here.
Anyway, one could change that solution a little to make something more general:
type IfUniqueProperties<T, Y = T, N = never> = unknown extends {
[K in keyof T]-?: T[K] extends Omit<T, K>[Exclude<keyof T, K>] ? unknown : never
}[keyof T] ? N : Y
Here, IfUniqueProperties<T, Y, N>
checks T
to see if its properties are all unique; if so, it returns Y
: if not, it returns N
. All I did was change the never
in the other solution to the generic N
, and the T[keyof T]
to the generic Y
. I also made N
default to never
and Y
default to T
, since this is often what people want with these sort of "check" types. Let's make sure it works:
type U = IfUniqueProperties<{a: 1, b: 2}, "unique", "duplicates">
// type U = "unique"
type D = IfUniqueProperties<{a: 1, b: 1}, "unique", "duplicates">
// type D = "duplicates"
So we can write RecToDU<T>
from the other answer in terms of IfUniqueProperties
:
type RecToDU<T> = IfUniqueProperties<T, T[keyof T]>;
type ZY = RecToDU<{ a: "z", b: "y" }> // "z" | "y"
type NV = RecToDU<{ a: "z", b: "z" }> // never
We do want never
so I left N
as the default, but for Y
you want T[keyof T]
instead of T
. So IfUniqueProperties
is a generalized version of RecToDU
.
Therefore, to answer this question, we can take the Reverser<T>
type from this answer and write UniqueReverser<T>
as follows:
type UniqueReverser<T extends Record<keyof T, PropertyKey>> =
IfUniqueProperties<T, Reverser<T>>;
Let's test it:
const VALUES = {
field1: "fieldA",
field2: "fieldB",
field3: "fieldC"
} as const
type UReversed = UniqueReverser<typeof VALUES>
/* type UReversed = {
readonly fieldA: "field1";
readonly fieldB: "field2";
readonly fieldC: "field3"; */
and:
const VALUES = {
field1: "fieldA",
field2: "fieldB",
field3: "fieldA"
} as const
type UReversed = UniqueReverser<typeof VALUES>
// type UReversed = never
Looks like what you wanted.