'Is there a way to make an array of SingleKey with all different keys
This question starts off from the answer to this question:
https://stackoverflow.com/a/60807986/13809150
Is there a way to make an array of SingleKey with all different keys? Doing Array<SingleKey<T>> OR SingleKey<T>[] makes it so they all need the same key. (e.g. [{key1: "value1"},{key2:"value1"}] throws and error because key1!==key2. Which is sadly the exact opposite behavior that I am looking for.
This is what I am trying to do/have done:
// From https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
// From: https://stackoverflow.com/a/53955431
type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;
type SingleKey<T> = IsUnion<keyof T> extends true ? never : {} extends T ? never : T;
function f<T extends Record<string, any>>(obj: SingleKey<T>[]) {
// console.log({ obj });
}
f([{}]); // errors here! This is correct
f([{ x: 5 }, { x: 5 }]); // Should have error because same key (NEED TO FIX)
f([{ x: 5 }, { y: 6 }]); // Should not have error because of opposite keys (NEED TO FIX)
f([{ x: 5}, { y : 6 }, { y : 4}]) // Should fail because duplicate key
The above code in a ts playground
Solution 1:[1]
I don't think that this is possible without using tuple types and casting your arrays to readonly tuples. The reason for this is because you don't know if any type in the union of the generic is repeated to ensure that { y: number } is repeated over and over again because the type is simply { x: number } | { y: number }.
f([{ x: 5}, { y : 6 }, { y : 4}])
However, here is my solution if you are ok with using recursive tuple mapper types and casting your arrays to readonly via as const:
/** A helper type to enforce constraints in conditional type clauses. */
type Is<T extends K, K> = T;
type EnsureSingleKey<
T extends readonly Record<string, unknown>[],
$Acc extends Record<string, unknown> = {},
> = T extends readonly [
Is<infer $First, Record<string, unknown>>,
...Is<infer $Rest, readonly Record<string, unknown>[]>,
] ? keyof $First extends keyof $Acc ? never
: EnsureSingleKey<$Rest, $Acc & $First>
: true;
function f<T extends readonly Record<string, unknown>[]>(
_obj: EnsureSingleKey<T> extends never ? never : T,
) {
}
// Argument of type 'readonly [{}]' is not assignable to parameter of type 'never'.
f([{}] as const);
// Argument of type 'readonly [{ readonly x: 5; }, { readonly x: 5; }]' is not assignable to parameter of type 'never'.
f([{ x: 5 }, { x: 5 }] as const);
// Argument of type 'readonly [{ readonly x: 5; }, { readonly y: 6; }, { readonly y: 4; }]' is not assignable to parameter of type 'never'.
f([{ x: 5 }, { y: 6 }, { y: 4 }] as const);
// Correctly passes!
f([{ x: 5 }, { y: 6 }] as const);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | sno2 |
