I would like to get types inferred for the init key in my BigObj objects created by a function. I'd like to provide the nums and strs keys and have the init signature have it's arguments and return type determined by nums and strs.
type NumMap = { [key: string]: number };
type StrMap = { [key: string]: string };
type UnknownMap<T> = {[k in keyof T]: unknown}
type InitFn<T1 extends NumMap, T2 extends StrMap> =
(from: UnknownMap<T1>) => UnknownMap<T2>;
type BigObj<N extends NumMap, S extends StrMap> = {
nums: N;
strs: S;
init: InitFn<N,S>
}
function makeBigObj<B extends BigObj<NumMap, StrMap>>(
opts: Partial<B>
): B {
return {
nums: {} as NumMap,
strs: {} as StrMap,
init: (() => ({})) as InitFn<NumMap, StrMap>,
...opts
} as B;
}
But the init signature is not inferred properly:
makeBigObj({
nums: { a: 1 },
strs: { b: "a" },
init: function(from) {
from.a; // should be inferred to be present
from.x; // should produce error
return {
b: [], // should be inferred to be required
y: [] // should produce error
}
}
})
I thought because I control the makeBigObj function it may infer the types better if I wrote it like this:
function makeBigObj2<B extends BigObj<NumMap, StrMap>>(
opts: Partial<Omit<B, "init">>,
initFn: B["init"]
): B {
return {
nums: {} as NumMap,
strs: {} as StrMap,
init: initFn,
...opts
} as B;
}
But the result is the same:
makeBigObj2({
nums: { a: 1 },
strs: { b: "a" }
}, function(from) {
from.a; // should be inferred to be present
from.x; // should produce error
return {
b: [], // should be inferred to be required
y: [] // should produce error
}
})
How do I get init to be inferred to have the a and b keys and not the x and y ones?