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.