Home > Software engineering >  How to access nested optional indexes from interface
How to access nested optional indexes from interface

Time:04-07

Given the following interface described on this TS playground

export type GetTestimonialsSectionQuery = { 
  __typename?: 'Query', 
  testimonialsSection?: { 
    __typename?: 'TestimonialsSection', 
    id: string, 
    testimonials: Array<{ __typename?: 'Testimonial', id: string, text: string, author?: { __typename?: 'TestimonialAuthor', id: string, name: string, photo: { __typename?: 'Asset', url: string } } | null }> 
} | null };

I would like to access the testimonials index from the whole interface by using the indexed access type, however, it doesn't work when the strictNullChecks compiler option is set and the result is any

Since testimonials can be undefined, would a type check needed before accessing the nested property?

CodePudding user response:

You can only perform an indexed access of the form T[K] when K is definitely a key type for K; that is, when K extends keyof T. If T is a union type then the only definite keys of T are those keys present on every member of the union. That is, keyof (A | B | C) becomes (keyof A) & (keyof B) & (keyof C).

In your case, since GetTestimonialsSectionQuery['testimonialsSection'] is a union of an object type with both null and undefined, you won't be allowed to index into it with anything that isn't also a key of null and undefined... and null and undefined have no keys; you can't index into them at all. Hence the error:

type Bad = GetTestimonialsSectionQuery[
    'testimonialsSection']['testimonials'] // error!
// ----------------------> ~~~~~~~~~~~~~~
// Property 'testimonials' does not exist on type ...

If all you care about is the object type and not the null and undefined, you can use a utility type to filter the union to just the object type before indexing into it. In general, you can filter unions via the Exclude<T, U> utility type, but for the specific and common case of removing null and undefined, you can use the NonNullable<T> utility type:

type Testimonials = NonNullable<
    GetTestimonialsSectionQuery['testimonialsSection']
>['testimonials']
/* type Testimonials = {
    __typename?: "Testimonial" | undefined;
    id: string;
    text: string;
    author?: {
        __typename?: "TestimonialAuthor" | undefined;
        id: string;
        name: string;
        photo: {
            __typename?: 'Asset';
            url: string;
        };
    } | null | undefined;
}[] */
type Testimonials = NonNullable<
    GetTestimonialsSectionQuery['testimonialsSection']
>['testimonials']
/* type Testimonials = {
    __typename?: "Testimonial" | undefined;
    id: string;
    text: string;
    author?: {
        __typename?: "TestimonialAuthor" | undefined;
        id: string;
        name: string;
        photo: {
            __typename?: 'Asset';
            url: string;
        };
    } | null | undefined;
}[] */

Looks good!

Playground link to code

  • Related