Home > Back-end >  is it possible to skip depth level in typescript?
is it possible to skip depth level in typescript?

Time:03-21

I recently started doing typescript and I was curious about skipping level depth, is it possible to skip depth level in typescript?

// from
A -> B -> C

// to
A -> C

const sample = {
    A: {
        B: {
            C: "one",
        }
    },
};

type Make<A, Z> = {
    [B in keyof A]: {
        [C in keyof A[B]]: {
            [D in keyof A[B][C]]: Z;
        }
    };
};

function foo<T>(object: T): Make<T, number> {
    return object as any;
}

// outputs: one
console.log(foo(sample).A.B.C);

what if we want to skip one level like this?

// expected output: one
console.log(foo(sample).A.C);

I tried to do this but it didn't work

type Make2<A, Z> = {
    [B in keyof A]: {
        [D in keyof A[B][]]: Z;
    };
};

function foo2<T>(object: T): Make2<T, number> {
    return object as any;
}

// doesnt work
console.log(foo2(sample).A.C);

CodePudding user response:

You can simply skip the middle layer by using key remapping and conditional typing.

We iterate over the values of the object with the remapping accessor in [K in keyof T], checking if its children T[K] are objects, if so, we return the inferred child of the T[K], Item, otherwise we just return T[K]

type SkipFirstDepth<T extends Record<string, any>> = {
  [K in keyof T]: T[K] extends Record<string, infer Item> ? Item : T[K];
};

type Make = SkipFirstDepth<typeof sample>

Then it's typing will be

{
    A: {
        C: string;
    };
}

Alternatively, if you want to skip every other layer all the way down you'll have to use recursive types.

type SkipEveryOtherDepth<T extends Record<string, any>> = {
  [K in keyof T]: T[K] extends Record<string, infer Item>
    ? Item extends Record<string, any>
      ? SkipEveryOtherDepth<Item>
      : Item
    : T[K];
};

type Example = SkipEveryOtherDepth<{
  foo: {
    bar: {
      foo: {
        bar: {
          foo: 'bar';
        };
      };
    };
  };
}>;

Then the final type will be

{
    foo: {
        foo: {
            foo: 'bar';
        };
    };
}

I did this off the cuff, so it may not work in every object shape but definetly works in these given examples.

Check these examples in action at the playground

  • Related