In typescript we already have ability to use recursive partial: Recursive Partial<T> in TypeScript
Is it possible to create the same generic but with ability to set depth
level for it?
For example:
interface RecursivePartialWithDepth<T, Depth extends number = 0> {
...implementation
}
So that we can use it for arbitrary level of depth:
type p1 = {
a: number;
};
type p2 = {
b: p1;
c: number;
};
type p3 = {
d: p2;
e: number;
};
type PartialWithLevelTwo = RecursivePartialWithDepth<p3, 2>;
/* and it will result to:
{ d?: { b?: { a: number }, c?: number }, e?: number }
Note that `a` is required!
*/
And if it is possible, then how to create such a Generic?
CodePudding user response:
Solution:
type Increment<A extends number[]> = [...A, 0];
type DeepPartial<
T,
Depth extends number = 0,
CurrentDepth extends number[] = []
> = T extends PrimitiveType
? T
: CurrentDepth["length"] extends Depth
? T
: {
[K in keyof T]?: DeepPartial<T[K], Depth, Increment<CurrentDepth>>;
};
type PartialWithLevelTwo = DeepPartial<p3, 2>;
const partialItem: PartialWithLevelTwo = {};
type PrimitiveType =
| string
| number
| boolean
| undefined
| null
| symbol
| bigint;
Explanation:
We first define a utility type Increment
that increments the length of the provided tuple.
Then we define DeepPartial
; it takes T
, the type to operate on, Depth
, the desired depth (by default it is 0), and CurrentDepth
, the current depth we are at.
If T
extends a primitive, we just return T
.
However, if it isn't it must be an object of some sort.
If the current depth we are at exceeds the depth limit, we stop and return T
.
If we can still go on, we do what Partial
does.
The only thing different is that instead of T[K]
we use DeepPartial
again, provide the same depth, and increment the current depth.
A fair note: because this expects number[]
as the depth (using the length of the array as our number here), you must provide [0, 0]
if you want a depth of 2
etc.
You can however, write a type that can create an array of length N filled with 0's.
Here's a version that uses numbers instead of arrays but is limited by Increment's range