Unlocking the potential of nested conditional types in TypeScript

The source of the autogenerated type below stems from a GraphQL query:

export type OfferQuery = { __typename?: 'Query' } & {
  offer: Types.Maybe<
    { __typename?: 'Offer' } & Pick<Types.Offer, 'id' | 'name'> & {
        payouts: Array<
          { __typename?: 'Payout' } & Pick<
            Types.Payout,
            'weGet' | 'theyGet'
          > & {
              offer: { __typename?: 'Offer' } & Pick<Types.Offer, 'id'>;
              publisher: Types.Maybe<
                { __typename?: 'Publisher' } & Pick<Types.Publisher, 'id'>
              >;
              eventType: Types.Maybe<
                { __typename?: 'EventType' } & Pick<
                  Types.EventType,
                  'id' | 'name'
                >
              >;
            }
        >;
      }
  >;
};

I am interested in utilizing parts of the OfferQuery type within my react component, specifically a payout element.

type Payout = OfferQuery['offer']['payouts'][number];

Unfortunately, I encounter an error

"Property 'payouts' does not exist on type 'Maybe{ __typename?: "Offer" | undefined; }..."
.

Could you suggest a workaround to tackle this issue and still access the payouts definition?

tsconfig.json

{
  "compilerOptions": {
    "esModuleInterop": true,
    "isolatedModules": true,
    "jsx": "react-jsx",
    "module": "esnext",
    "moduleResolution": "node",
    "noEmit": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "strict": true,
    "target": "esnext"
  }
}

Typescript 4.1.3

Answer №1

Summary

type Payout = NonNullable<OfferQuery['offer']>['payouts'][number];

To avoid nullability issues, wrap OfferQuery['offer'] with NonNullable first before accessing payouts.


Deciphering the error-message

The error message lacks clarity regarding the meaning of Maybe.

The definition of Maybe states:

type Maybe<T> = T | null;

This implies that trying to access payouts from null leads to the error, as null does not contain payouts. This is the crux of the issue conveyed by the error message.

GraphQL codegen and nullability

GraphQL codegen incorporates Maybe for nullable fields in your GraphQL schema. Thus, utilizing OfferQuery['offer'] results in receiving both null and the actual value. Specifically, you receive:

type ExampleWithMaybe = null | {
  payouts: { weGet: number; theyGet: number }[];
  /* ...remainder of the definition... */
};

Rather than the anticipated:

type ExampleWithoutMaybe = {
  payouts: { weGet: number; theyGet: number }[];
  /* ...remainder of the definition... */
};

Hence, when dealing with payouts in OfferQuery['offer'], remember to exclude the null beforehand due to the absence of payouts in null.

The resolution: Eliminate null

To obtain the type definition sans null, utilize the type utility NonNullable, as demonstrated below:

type ExampleWithMaybe = null | {
  payouts: { weGet: number; theyGet: number }[];
  /* ...remainder of the definition... */
};
type ExampleWithoutMaybe = NonNullable<ExampleWithMaybe>;

Applying this concept to your scenario:

// ❌ `payouts` does not exist on `null`
// type Payout = OfferQuery['offer']['payouts'][number];

// ✅
type Payout = NonNullable<OfferQuery["offer"]>["payouts"][number];

Answer №2

It seems like the most effective approach is to remove any null or undefined values before trying to access the type of array element:

type SelectedPayout = Exclude<OfferQuery['offer']['payouts'], null | undefined>[number];

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

Incorporating a d3 chart into an Angular 4 project

Currently, I am in the process of developing an Angular application using TypeScript. My aim is to incorporate a force directed network graph from Mike Boston built with d3, which can be viewed here. After successfully translating most of the code to Type ...

Efficiently organizing items within a list on Ionic

Currently, I have an ion-list structured as follows: <ion-list *ngFor = "let chat of Chats"> <ion-item (click) = "openChat(chat.id)"> <ion-label> <h2> {{chat.username}} </h2> ...

Why am I encountering a 400 error with my mutation in Apollo Client, when I have no issues running it in Playground?

After successfully testing a mutation in the playground, I attempted to implement it in my Apollo client on React. However, I encountered an error message stating: Unhandled Rejection (Error): Network error: Response not successful: Received status code 40 ...

Sending variables from a main page to a nested component

Currently facing an issue with the routing mechanism in Angular 9. Specifically, I am struggling to capture the parameter inside the BuildingDetailComponent even though it is present in the URL displayed in the address bar. In the Parent component, my rou ...

Crack encrypted information using Typescript after it was encoded in Python

I've encountered an issue while attempting to decrypt previously encrypted data in Python. Here is how I encrypt the data in Python: iv = Random.new().read( AES.block_size ) cipher = AES.new(secret_key, AES.MODE_CBC, iv) encrypdata = base64.b64enco ...

The function signature '(newValue: DateRange<dateFns>) => void' does not match the expected type '(date: DateRange<unknown>, keyboardInputValue?: string | undefined) => void' as per TypeScript rules

I'm currently utilizing the MUI date range picker from https://mui.com/x/react-date-pickers/date-range-picker/. Here's my code snippet: <StaticDateRangePickerStyled displayStaticWrapperAs="desktop" value={valu ...

What's the reason for Angular's Tour of Heroes HTTP error handler allowing arguments of any type?

While following Angular's tour of hero tutorial, I noticed that the author implemented an error handler for the http service (hero-service). What struck me was the use of 'any' as the type for the error argument in the handler, whereas in ot ...

Dynamic Material UI Timeline

I am facing a challenge with making the Timeline in Material UI responsive for the first time. Currently, I have it set to align 'alternate', but I want it to switch to align 'left' when viewed on mobile or certain screen widths. I have ...

Using a snapshot test with MenuItem from material-ui is not compatible with Jest

I am facing an issue while trying to perform a snapshot test with jest on a component that utilizes typescript, react, and material-ui. Specifically, the MenuItem component from material-ui is throwing an Invariant Violation: getNodeFromInstance: Invalid a ...

Guide to leveraging tanstack table v8 for sorting data within a specific date range

Received data from API: const abc = [ { date: '2023-12-8', value: 'mop' },{ date: '2023-10-8', value: 'qrs' } ] How can we create a date range using two input fields when the dates are in string forma ...

Enhance your coding experience with code completion and autocomplete in Angular/Typescript using ATOM within

Is it possible to have Codecompletion / Autocomplete in Atom similar to Webstorm? Currently I am getting familiar with TypeScript and really enjoying it, but the lack of Codecompletion support for my HTML files in Atom is quite frustrating. Having this f ...

CLI package enables exporting API facilities

I have a mono repository containing a CLI package (packages/cli) and a web application (apps/web). I want to utilize the public API of the CLI within the web app. The CLI package is packaged with tsup: export default defineConfig({ clean: false, dts: ...

What is the correct syntax for declaring a variable within a switch statement in TypeScript?

How can I properly use a switch statement in TypeScript to assign a new variable a value? For example: let name: string switch(index) { case 0: name = "cat" case 1: name = "dog" .... } I keep getting the err ...

Discovering the worth of a variable outside of a subscription or Promise within Ionic 3

Apologies for my English. I am encountering an issue when attempting to view the results of a REST API using both subscribe and Promise methods. Within my provider, I have the following code: Provider: import { HttpClient } from '@angular/common/h ...

What's the best way to maintain the return type of a function as Promise<MyObject[]> when using forEach method?

I am currently working with a function called search, which at the moment is set up to return a type of Promise<MyObject[]>: export function search(args: SearchInput) { return SomeInterface.performSearch(args) .then(xmlRequest =&g ...

Leveraging Angular2's observable stream in combination with *ngFor

Below is the code snippet I am working with: objs = [] getObjs() { let counter = 0 this.myService.getObjs() .map((obj) => { counter = counter > 5 ? 0 : counter; obj.col = counter; counter++; return view ...

Is Typescript the Ultimate Replacement for propTypes in React Development?

After diving into Typescript for the first time and exploring related articles, it appears that when using Typescript with React, propTypes definitions may no longer be necessary. However, upon examining some of the most popular React Component Libraries: ...

The property in the object cannot be assigned because it is read-only: [object Object]

I am currently developing an Ionic application using the Angular framework and NGRX. I have encountered an issue with a selected checkbox. The problem arises when: First, I fetch a list of vehicles from a database using a service. Then, I set the propert ...

Injectable error occurred while injecting one @Injectable() into another

I'm encountering an issue with Angular2 Dependency Injection. When attempting to inject one class into another, I am receiving the following error: Error Message: "Cannot resolve all parameters for 'ProductService'(undefined). Make sure tha ...

Next.js Enhanced with Google Analytics

I've been experimenting with integrating Google Analytics into Next.js, following a tutorial on YouTube - https://www.youtube.com/watch?v=lMSBNBDjaH8 Following the instructions in the video, I added these two scripts in _document.js: <script async ...