I have these general definitions:
type Module<P extends Payloads, C extends Children> = {
payloads: P;
children: C;
};
type Children = Record<string, any>; // some values will be nested Modules
type Payloads = Record<string, any>;
type ExtractPayloads<M extends Module<any, any>> = M extends Module<infer P, any> ? P : never;
type ExtractChildren<M extends Module<any, any>> = M extends Module<any, infer C> ? C : never;
Basically, Modules are types that specify a children
type, which can contain nested Modules.
I have this type that can generate Action
s based on a Module's payload
type:
type ModuleRootActions<
MODULE extends Module<any, any>,
PAYLOADS = ExtractPayloads<MODULE>,
> = {
[NAME in keyof PAYLOADS]: {
type: NAME;
payload: PAYLOADS[NAME];
};
}[keyof PAYLOADS];
Next, I have a recursive type that helps me generate Action
s for ALL Modules in a Module's tree (i.e., including its child modules, and grandchildren, etc):
type AllModuleActions<
MODULE extends Module<any, any>,
CHILDREN = ExtractChildren<MODULE>,
> =
| ModuleRootActions<MODULE>
| {
[KEY in keyof CHILDREN]: CHILDREN[KEY] extends Module<any, any>
? AllModuleActions<CHILDREN[KEY]>
: never;
}[keyof CHILDREN];
Finally, I have these concrete examples:
type C = Module<{
"incrementBy": number;
}, {}>;
type B = Module<{
"setIsSignedIn": boolean;
}, {
c: C;
}>;
type A = Module<{
"concat": string;
"setIsDarkMode": boolean;
}, {
b: B;
}>;
All my types thus far are correct -- I've verified this manually. Now, I'm writing a function that takes in an Action
of a generic Module
. I can successfully define these types:
type ConcreteAction<M extends Module<any, any>> = AllModuleActions<M>;
const concreteAction: ConcreteAction<A> = {
type: "concat",
payload: "str",
}
But once I try to put them in a generic function, I get the error in the title.
const composedReducer = <MODULE extends Module<any, any>>(
action: AllModuleActions<MODULE>,
) => {
if (action) {
}
};
You'll notice in the Playground link that action
has the error: "Type instantiation is excessively deep and possibly infinite". I assume this is happening because the MODULE
type is generic, and it's possible that there could be cycles in the Module
definition, even though semantically I know that it's a tree.
How can I fix this error? Is there a way to tell the compiler that the graph will always be a tree and never contain infinite cycles?
AllModuleActions
is recursive in a way that the compiler can't handle well; it's like thePaths
/Leaves
type in this answer in that it builds a big union out of arbitrarily nested input types. You can try to defer the evaluation like this (in that example I removed a lot of indirection to make it more of a minimal reproducible example, but the behavior is the same). Or you can do an explicit depth limiter like this. Let me know if you want either of these as an answer. – Vendee