I want to extract a sub-type from an interface in typescript like below
interface components {
schemas: {
a: {
b?: Array<{
c?: {
d?: string;
};
}>;
};
}
// I want to export
export type custom = components["schemas"]["a"]["b"][number]["c"]
however I get an error at [number]
saying Type '{ c?: { d?: string | undefined; } | undefined; }[] | undefined' has no matching index signature for type 'number'.ts(2537)
How do we achieve this?
CodePudding user response:
Here is what I came up with:
type Increment<A extends number[]> = [...A, 0];
type DeepRequired<T, D extends number = -1, R extends number[] = []> =
T extends object ? R["length"] extends D ? T : {
[K in keyof T]-?: DeepRequired<T[K], D, Increment<R>>;
} : T;
Let's look at DeepRequired
without the extra bits:
type DeepRequired<T> = T extends object ? {
[K in keyof T]-?: DeepRequired<T[K]>;
} : T;
This type is simple at its core; it's simply traversing the entire object and making all properties required.
This does work without errors:
type T = DeepRequired<components>["schemas"]["a"]["b"][number]["c"];
But you'll notice T
's "d" property is required too, which might not be the desired result. That's why it's a lot more complicated; it limits the depth at which it makes properties required.
Back to our original snippet, a utility type Increment
is created to increment the depth we are at, and the new parameters for DeepRequired
include the specified depth (optional), and the current depth it is at.
Before it makes all properties required, it checks if it exceeds the depth specified, and if it does, it stops.
This now works as expected:
type T = DeepRequired<components, 5>["schemas"]["a"]["b"][number]["c"];
CodePudding user response:
I really like @kellys' answer for the part of the DeepRequired, but if the goal for the result is string | undefined
, I don't like the part where a number (the depth) has to be hardcoded so I can suggest the following approach
type Path<T, K> = K extends [any, ...any]
? (K extends [keyof NonNullable<T>, ...infer RR]
? Path<NonNullable<T>[K[0]], RR>
: never)
: T
export type custom = Path<components, ['schemas', 'a', 'b', number, 'c', 'd']>