Home > Net >  Typescript type recursion over object
Typescript type recursion over object

Time:12-31

I have these definitions for a Module, which contains a MetaData type and an object that maps arbitrary names to child/nested Modules:

type Module<T, C extends Children> = {
    metaData: T;
    children: C;
};

type Children = {
    [key: string]: Module<any, any>;
}

type ExtractMetaData<M extends Module<any, any>> = M extends Module<infer T, any> ? T : never;

type ExtractChildren<M extends Module<any, any>> = M extends Module<any, infer C> ? C : never;

Now, let's say I define three simple modules, A, B, and C, so that C is a child of B and B is a child of A:

type C = Module<number, {}>;

type B = Module<boolean, {
  c: C;
}>;

type A = Module<string, {
  b: B;
}>;

What I want is a utility type AllMetaData<T extends Module> that will return a union of all the MetaData types in a Module tree. For example, AllMetaData<C> is the type number, AllMetaData<B> is the type number | boolean, and AllMetaData<A> is the type number | boolean | string.

Here's what I have:

type AllMetaData<
  MODULE extends Module<any, any>,
  CHILDREN = ExtractChildren<MODULE>,
  METADATA = ExtractMetaData<MODULE>,
> =
  | METADATA
  | {
      [KEY in keyof CHILDREN]: CHILDREN[KEY] extends Module<any, any>
        ? ExtractMetaData<MODULE>
        : never;
    }[keyof CHILDREN];

But it doesn't seem to work, because when I define this type:

type Result = AllMetaData<A>;

Result is equivalent to string, when it should be a union of all the MetaData types in A's tree.

Why doesn't my AllMetadata type work?

Playground link

CodePudding user response:

You have 2 mistakes.

  1. line 9 should use CHILDREN[KEY] instead of MODULE
  2. line 9 should use AllMetaData instead of ExtractMetaData

Playground

  • Related