简体   繁体   中英

how to access nested array type in typescript

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) Type '{ c?: { d?: string | undefined; } | undefined; }[] | undefined' has no matching index signature for type 'number'.ts(2537)

How do we achieve this?

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"];

Playground

I really like @kellys ' answer for the part of the DeepRequired, but if the goal for the result is string | undefined 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']>

TS Playground

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM