Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Null instead of Undefined #36

Closed
VienDinhCom opened this issue Oct 8, 2020 · 16 comments
Closed

Null instead of Undefined #36

VienDinhCom opened this issue Oct 8, 2020 · 16 comments

Comments

@VienDinhCom
Copy link

VienDinhCom commented Oct 8, 2020

This is the image field in Shema.
image

This is the image field which is generated by genql. It must be null, not undefined. Do you have an option to config it?

See also: graphql/graphql-js#1298 (comment)

image

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}

Now I have to use this function to convert null to undefined:

import traverse from 'traverse';

function nullToUndefined<D extends any>(data: D) {
  return traverse(data).map(function (value) {
    if (value === null) this.update(undefined);
  }) as D;
}
@remorses
Copy link
Owner

remorses commented Oct 8, 2020

Can you show me your use case? where are you using nullToUndefined?

@VienDinhCom
Copy link
Author

VienDinhCom commented Oct 8, 2020

@remorses You can check it here:

import traverse from "traverse";
import { createClient } from "./generated";

function nullToUndefined<D extends any>(data: D) {
  return traverse(data).map(function (value) {
    if (value === null) this.update(undefined);
  }) as D;
}

const client = createClient();

(async () => {
  const data = await client.query({
    users: {
      email: true,
      image: true,
    },
  });

  const { users } = nullToUndefined(data);

  const { image } = users[0]; // const image: string | undefined
})();

@remorses
Copy link
Owner

remorses commented Feb 9, 2021

I used undefined to simplify the typescript types, tell me if you find any problem where null should be used instead

@remorses remorses closed this as completed Feb 9, 2021
@nullndr
Copy link

nullndr commented Mar 28, 2023

Hey @remorses, I have a use case where null should be used instead of undefined.

I have the entity customer from Shopify that is primary parsed via webhooks, where I have some fields as null.

I also have to retrieve some data from orders ecc, but the two are incompatible since the types from JSON rest are string | null while the types from genql are string | undefined.

Until now, I simply use the ?? operator to transform undefined values into null, but I don't like this, since I should be able to just use null, here an example:

await processAutomations.queue.add(
  "process automations of type ORDER_CONFIRMATION",
  {
    customer: {
      admin_graphql_api_id: customer.id,
      created_at: customer.createdAt,
      email: customer.email ?? null,
      first_name: customer.firstName ?? null,
      last_name: customer.lastName ?? null,
      phone: customer.phone ?? null,
      tags: customer.tags,
      updated_at: customer.updatedAt,
      sms_marketing_consent:
        smsMarketingConsent == null
        ? null
        : {
          state: smsMarketingConsent.marketingState,
          consent_updated_at:
            smsMarketingConsent.consentUpdatedAt ?? null,
          consent_collected_from:
            smsMarketingConsent.consentCollectedFrom ?? null,
      },
      default_address: {
        country,
        country_code: countryCodeV2,
      },
    },
    type: "ORDER_CONFIRMATION",
    shopifyDomain,
  },
);

I understand that undefined helps you with typescript types, but I really can't see why I (as a library client) should have a mismatched type.

Apart my example, take the example where I should save the data from the genql client into a database, maybe with an orm like prisma. Prisma use null, not undefined, so I should transform the data before save it. This is unnecessary and rendondant

@RobbeCl
Copy link

RobbeCl commented Apr 11, 2023

Why is this closed? There is a mismatch between the types and what graphql returns.

@remorses remorses reopened this Apr 11, 2023
@remorses
Copy link
Owner

i will add null to the types soon

@RobbeCl
Copy link

RobbeCl commented Apr 11, 2023

Thanks @remorses!

@ayoung19
Copy link

hey @remorses, we've ran into the same problem for mutations where we base the mutation inputs on the return of a graphql query (where null sets it as null and undefined doesn't change the field at all).

is there an eta on this behavior, would you like some help with it?

@remorses
Copy link
Owner

Released in 4.0.0

@ayoung19
Copy link

@remorses this is awesome! thank you so much this is a huge win for our use case.

however i think there's a small bug and response fields are still incorrectly typed as undefinable with ?. i think request fields should definitely keep that behavior though because undefined represents opting out of mutating the field and leaving it unchanged (Optional on a python backend) while null actually sets it to NULL on the database (None on a python backend). however a query should never respond with undefined

image

image

@remorses
Copy link
Owner

remorses commented Apr 24, 2023

If you want to remove the undefined values from a type simply use Required<Type>

I don't want to remove undefined from the response fields, undefined makes reusing types much easier, for example

let repository: Repository = {
  name: 'x'
}

instead of

let repository: Repository = {
  name: 'x',
  anotherField: null,
  someOtherField: null,
  // ...
}

@ayoung19
Copy link

ayoung19 commented Apr 24, 2023

hm great suggestions with Required, but it doesn't really recurse well unless we write another helper.

can you elaborate on the problem with removing undefined from response fields? i don't see the issue with the latter if that's the return of an endpoint.

@remorses
Copy link
Owner

remorses commented Apr 25, 2023

@ayoung19 can you show me an example where having undefined in the type is a problem?

@nullndr
Copy link

nullndr commented Apr 25, 2023

@remorses as an example you can take the one I posted above.

@ayoung19
Copy link

ayoung19 commented Apr 26, 2023

@remorses well the core problem is the type returned by graphql is null, not undefined. we've actually ran into bugs where we tried to destruct and assign a default value to an incoming object from our backend which typescript would incorrectly let us do due to the type annotations, something like this:

const { name = 'John Doe' } = person; // typed as { person: string | undefined }
name.split(' '); // runtime error, person.name is null even though it's typed as string | undefined

beyond that, we also have form state code that looks like what @nullndr posted, we have to add || null to all of the incoming fields so that they are properly typed as null, so on mutations we can send the object back. we can't store it as undefined because undefined means opt out of changes in the mutation input

@remorses
Copy link
Owner

I ended up removing the undefined types in version 5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants