When working with recursive types, there is a dependency on order. By casting Person to Unions and converting it with EntriesOf, the conversion of Person
to:
{ id: string } | { name: string } | { lastname: string }
must be done.
Subsequently, you can cast the union to an array with specific requirements for each type using UnionToTuple
. This results in the conversion to
[{ id: string }, { name: string }, { lastname: string }]
.
This process ensures that every property is present on the input, but it does rely on order (however, this can be resolved).
type EntriesOf<Type> = { [Key in keyof Type]: Pick<Type, Key> }[keyof Type];
type UnionToTuple<T> = (
(
(
T extends any ? (t: T) => T : never
) extends infer U ?
(U extends any ? (u: U) => any : never
) extends (v: infer V) => any ? V : never
: never
) extends (_: any) => infer W ?
[...UnionToTuple<Exclude<T, W>>, W]
: []
);
type Person = {
id: string;
name: string;
lastname: string;
};
function fromPieces<T>(arg: UnionToTuple<EntriesOf<T>>): T {
return {} as T;
}
// Example of successful use
const test = fromPieces<Person>([
{ id: "" },
{ name: "" },
{ lastname: "test" }
])
// Example that will not work
const test1 = fromPieces<Person>([
{ name: "" },
{ id: "" },
{ lastname: "test" }
])
View live demo
Credits to https://github.com/microsoft/TypeScript/issues/13298#issuecomment-707364842 for the UnionToTuple
type.