0

The origin of the following autogenerated type is 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'
                >
              >;
            }
        >;
      }
  >;
};

Now I would like to reuse parts of the OfferQuery type in my react component, namely a payout element.

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

This however gives me an error "Property 'payouts' does not exist on type 'Maybe{ __typename?: "Offer" | undefined; }...".

Do you have any suggestions on how to circumvent this issue and access payouts definition anyway?

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
3
  • Where did Types come from ? I'm unable to reproduce. I'd willing to bet from fp-ts Commented Jan 13, 2021 at 10:06
  • I don't understand the question, but I am not using fp-ts. Commented Jan 13, 2021 at 10:18
  • Please share reproducable example Commented Jan 13, 2021 at 10:20

2 Answers 2

1

TL;DR

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

because Maybe causes OfferQuery['offer'] to be nullable, and so you need to wrap it with the NonNullable type util first:


Interpreting the error-message

The error-message is unclear, because it does not reveal what Maybe actually means.

The definition of Maybe, will say:

type Maybe<T> = T | null;

meaning when you try to get payouts from OfferQuery['offer'], you're also trying to get payouts from null (which doesn't exist). This is what the error-message is trying to communicate.

GraphQL codegen and nullable

GraphQL codegen will (correctly) add Maybe to each field that is nullable in your GraphQL schema. This has the implication that when you use the type OfferQuery['offer'] you will get both null AND the actual definition. I.e. you're actually receiving null together with the rest:

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

instead of receiving what you likely expected:

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

So when you want to use the type payouts on OfferQuery['offer'], you need to remember that you have to exclude the null-value first, because payouts doesn't exist on null.

The solution: Remove null

In order to get the type definition without null, you can use the type-util NonNullable, meaning we can rewrite ExampleWithoutMaybe from the above example like so:

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

and to make it specific to your example:

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

// ✅
type Payout = NonNullable<OfferQuery["offer"]>["payouts"][number];
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much NonNullable is more elegant then using Exclude.
0

Apparently the only solution is to exclude null and undefined before accessing the array element type:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.