Reputation: 3341
In the below example I try to map to a list of InnerNamed typed values from a lookup of typed values, (see InnerNamed definition) but the inferred type is way too broad.
In the end, the type of resolved
should be (InnerNamed<"Cefn", 3> | InnerNamed<"Joe", "hey">)[]
equivalent to ({Cefn:3} | {Joe:"hey"})[]
. It is instead inferred as InnerNamed<string, unknown>[]
Is there anything I can do to improve inference here?
See also the playground to experiment interactively.
/** Assert type of Object.entries */
type InferEntry<Lookup, K extends keyof Lookup = keyof Lookup> = {
[K in keyof Lookup]: [K, Lookup[K]];
}[K];
/** Type for an object containing a single, named entry (common paradigm for javascript records like
* ```
* {
* MyItem:{some:"value"}
* }
* ```
*/
type InnerNamed<InnerName extends string, T> = { [k in InnerName]: T };
const namedProps: InnerNamed<"Cefn", {hi:number}> = {
// autocompletes correctly
Cefn: {
hi: 3
}
};
/** Map to InnerNamed types given a lookup of named types. */
type Resolved<Name extends string, Resolver extends Record<Name, any>> = {
[N in Name]: InnerNamed<Name, Resolver[Name]>;
}[Name];
function createResolved<Name extends string, Resolver extends Record<Name, any>>(resolver:Resolver){
return Object.entries(resolver).map((entry) => {
const [name, value] = entry as InferEntry<typeof resolver>
return {
[name]:value
}
}) as Resolved<Name, Resolver>[]
}
// the type of resolved should be (InnerNamed<"Cefn", 3> | InnerNamed<"Joe", "hey">)[]
// i.e. ({Cefn:3} | {Joe:"hey"})[]
// but is instead InnerNamed<string, unknown>[]
const resolved = createResolved({
Cefn:3,
Joe:"hey"
});
// printing out the actual structure of resolved
// shows the narrower type would be satisfied
// even though it was not inferred
console.log({resolved})
// "resolved": [
// {
// "Cefn": 3
// },
// {
// "Joe": "hey"
// }
// ]
// }
Upvotes: 1
Views: 70