It might be a good idea to reorganize your data types in order to eliminate conditional types (which can be tricky for the compiler to handle) and utilize mapped types and inference from mapped types as much as possible. Your makeArrayed()
function seems to be in good shape as it is.
I propose defining SingleArgFunctionObject
and ArrayedReturnFunctionObject
as generics that operate on the object type AS
... the concept being that each property of AS
is treated akin to the A
in your SingleArgFunction<A>
and ArrayedReturnFunction<A>
:
type SingleArgFunctionObject<AS extends object> = {
[K in keyof AS]: SingleArgFunction<AS[K]>
}
type ArrayedReturnFunctionObject<AS extends object> = {
[K in keyof AS]: ArrayedReturnFunction<AS[K]>
}
With this in place, makeArrayedAll()
will be generic with respect to AS
. The challenging aspect to keep the compiler happy is ensuring that the for
loop treats each key
as a generic K extends keyof AS
. This can be achieved more easily by utilizing the forEach()
method of arrays:
function makeArrayedAll<AS extends object>(
fs: SingleArgFunctionObject<AS>
): ArrayedReturnFunctionObject<AS> {
const result = {} as ArrayedReturnFunctionObject<AS>;
(Object.keys(fs) as (keyof AS)[]).forEach(<K extends keyof AS>(key: K) => {
result[key] = makeArrayed(fs[key]);
})
return result;
}
This should generate the same outcome as your original code, but now the compiler has greater confidence that makeArrayed(fs[key])
of type
ArrayedReturnFunction<AS[K]>
can be assigned to
result[key]
of type
ArrayedReturnFunctionObject<AS>[K]
.
Hope this explanation helps! Best of luck!
Playground link