Issue
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);
Solution
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
Answered By – Cody Duong
Answer Checked By – Mary Flores (BugsFixing Volunteer)