I have an object similar to this:
const v = [
{ pointer: "/name", name: "N1" },
{ pointer: "/name", name: "N2" },
{ pointer: "/zip", name: "Z1" }
] as const
and I'd like to generate objects like this:
const m: M = {
"/name": [{ pointer: "/name", name: "N2" }] // can also contain N1, but not Z1
}
Is it possible to create a type for this? My attempt should better explain what I'm trying to do:
type V = typeof v;
type T = V[number]
type M = {
[K in T["pointer"]]: Array<T extends {pointer: K} ? T : never>;
}
codesandbox: https://codesandbox.io/s/56564
You're close, but the problem is that your T
is a concrete type and not a generic type parameter, and thus T extends { pointer: K } ? T : never
does not distributive over unions. So T extends { pointer: K }
will just be false for each choice of K
, and you get never[]
instead of what you're looking for.
The easiest way to get distributive conditional types back is to use a type alias of the form type Something<X> = X extends { pointer: K } ? X : never
and then plug in Something<T>
. Luckily, the built-in Extract<T, U>
utility type already works this way. So we can write this:
type M = { [K in T["pointer"]]?: Array<Extract<T, { pointer: K }>> };
which evaluates to:
/*
type M = {
"/name"?: ({
readonly pointer: "/name";
readonly name: "N1";
} | {
readonly pointer: "/name";
readonly name: "N2";
})[];
"/zip"?: {
readonly pointer: "/zip";
readonly name: "Z1";
}[];
}
*/
Which is closer to what you want, I think. Note that I made the props optional, since your m
constant doesn't have all the props:
const m: M = {
"/name": [{ pointer: "/name", name: "N2" }]
}; // okay
Okay, hope that helps; good luck!