Unraveling the mystery of nested optional indexes in interfaces

Discover the interface outlined 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 };

To retrieve the testimonials index from the entire interface, you can utilize the indexed access type. However, if the strictNullChecks compiler option is enabled, the outcome may be any.

If testimonials could potentially be undefined, a type check may be required before accessing the nested property.

Answer №1

When accessing an index in the form of T[K], make sure that K is indeed a key type for T; meaning, K extends keyof T. If T is a union type, then the definite keys of T are only those keys present on every member of the union. In this case, keyof (A | B | C) is represented as

(keyof A) & (keyof B) & (keyof C)
.

In your scenario, since

GetTestimonialsSectionQuery['testimonialsSection']
is a union containing an object type along with both null and undefined, you cannot access it using any key that is not also part of null and undefined. As null and undefined do not have keys to index into, hence the error message:

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

If you are interested in just the object type excluding null and undefined, you can use a utility type to filter the union to solely the object type before indexing. Typically, union types can be filtered using the Exclude<T, U> utility type, but specifically for removing null and undefined, you can utilize 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;
}[] */

Great job!

Playground link to code

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

TestCafe has encountered an issue: "There are no tests available to run. This may be due to either the test files not containing any tests or the filter function being too

Attempting to run automated tests using TestCafe resulted in an error when executing the following command. testcafe chrome testc.ts The specified command was used to test the testc.ts file within my Angular application, with TestCafe installed globally ...

The external typing file encounters an issue when trying to locate the relative path to its own index.d.ts file

While working on my project and using react-color as a dependency, I encountered an issue with the tsc import failing. The error message displayed: node_modules/@types/react-color/lib/components/sketch/Sketch.d.ts(2,41): error TS2307: Cannot find module & ...

Limit function parameters to only accept values with matching keys

I am relatively new to using TypeScript and am currently working on a project that involves handling various shapes of data from different sources. My goal is to pass this data to different aggregator classes, with one aggregator class corresponding to eac ...

The TypeScript compiler generates a blank JavaScript file within the WebStorm IDE

My introduction to TypeScript was an interesting experience. I decided to convert a simple JavaScript application, consisting of two files, into TypeScript. The first file, accounts.ts, contains the main code, while the second one, fiat.ts, is a support f ...

Storing a variable in Cypress with Typescript for use in the afterEach teardown step

Throughout my test cases, I store data in a variable to be used consistently. The variable maintains its value until the very end of the test, but when trying to access it in the @afterEach teardown function for global clean up, it appears empty. It seems ...

Adjusting the interface of a third-party TypeScript library

I am currently working on modifying a third-party interface. I'm curious about why this particular code is successful: import { LoadableComponentMethods as OldLoadableComponentMethods } from '@loadable/component'; declare module "load ...

Designing architecture for NPM packages in TypeScript

I am currently developing multiple NPM packages using TypeScript and I am exploring the most effective strategies for supporting various target architectures. When compiling to ES3, there is extensive support but it comes with additional boilerplate for c ...

Websites experiencing horizontal scrolling functionalities

I have noticed that in my angular project, the website becomes horizontally scrollable when I log in. This only happens after logging in, and does not occur beforehand. I'm using angular calendars and Bootstrap for styling. What could be causing this ...

Struggling to make Mongoose with discriminator function properly

I seem to be facing an issue with my schema setup. I have defined a base/parent Schema and 3 children schemas, but I am encountering an error message that says: No overload match this call Below is the structure of my schema: import { model, Schema } fr ...

Concealed Content Within Drawer Navigation

When using the Material UI permanent drawer component in different pages, I encountered an issue where the main content was getting hidden behind the drawer's toolbar and sidebar. I am unsure how to fix this problem. It seems like a styling issue, bu ...

Array of Typed Objects in TypeScript

Here is the setter I created in TypeScript: public set data(data: Array<Data>) { console.log(data[0].getterProperty); console.log(data[0] instanceof Data); console.log(typeof data[0]); this.setGridDataIfReady(); } If data contains only one ...

Guide to adjusting the color of Fluent UI icon when hovering with mouse?

I've been implementing Fluent UI in my current project. When initializing my button, I use this straightforward JavaScript code: iconProps: { iconName: 'NewFolder', styles: { root: { color: 'orang ...

Select specific columns from an array using Typescript

I have a collection of objects and I'm looking for a way to empower the user to choose which attributes they want to import into the database. Is there a method to map and generate a separate array containing only the selected properties for insertion ...

FabricJS Canvas with a React DropDown Feature

While I have successfully created a TextBox on FabricJS Canvas, creating a Dropdown component has proven to be a challenge. The fabric.Textbox method allows for easy creation of text boxes, but no such built-in method exists for dropdowns in FabricJS. If y ...

What is the correct way to invoke a static TypeScript class function in JavaScript?

Recently, I encountered a scenario where I have a TypeScript script called test.ts structured like this: class Foo { public static bar() { console.log("test"); } } The requirement was to call this TypeScript function from plain JavaScript ...

My goal is to develop a secure login system with authentication on the Angular platform

login(data: any) { this.user.getUsers().subscribe( (users) => { const user = users.find((u) => u.username === data.username && u.userpassword === data.password); if (user) { // Valid username and password, ...

typescript when an argument is missing, it will automatically be assigned

Here is my TypeScript function: function more(argv: {a: number, b?: string}): number{ console.log( b) return a } I am invoking the function this way: let arc = more({a: 5}) Unexpectedly, I see 10 in the console. I was anticipating undefined ...

Different ways to maintain the original syntax highlighting colors in JavaScript console

Upon closer inspection near the green arrows, you can see that the default console.log function colorizes values based on their type, distinguishing between string and number. https://i.sstatic.net/MtO8l.png In contrast, highlighted near the red arrows i ...

Exploring Typescript's type narrowing capabilities through destructuring

This code snippet is encountering errors: type Example = { x: true, y: null, z: null } | { x: false, y: Error, z: null } | { x: false, y: null, z: { val: number} } function getExample(): Example { return { x: false, y: null, z: { val ...

Error in TypeScript detected for an undefined value that was previously verified

I have developed a function that can add an item to an array or update an item at a specific index if provided. Utilizing TypeScript, I have encountered a peculiar behavior that is puzzling me. Here is the Playground Link. This simple TypeScript functio ...