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

Inconsistent undefined vs. null handling #926

Open
jtheisen opened this issue Sep 6, 2017 · 10 comments
Open

Inconsistent undefined vs. null handling #926

jtheisen opened this issue Sep 6, 2017 · 10 comments

Comments

@jtheisen
Copy link

jtheisen commented Sep 6, 2017

Optional values for

  • return values and
  • property values

are correctly configurable with the TypeScriptGeneratorSettings.NullHandling setting.

Parameter values, however, are always optional with null, never with undefined.

There is no deeper reason for this, is there?

@RicoSuter
Copy link
Owner

RicoSuter commented Sep 6, 2017

Yes, the method may need both values with the following semantics:

  • query "foo" with null value => added as null => url?foo=null (swagger: x-nullable = true)
  • query "foo" with undefined => not added => url (swagger: required = false)

@jtheisen
Copy link
Author

jtheisen commented Sep 6, 2017

So in Swagger-land you can have a parameter that

  • isn't required
  • may be null if specified
  • may be non-null if specified

Correct?

While TypeScript can reflect that, WebAPI doesn't I believe.

@RicoSuter
Copy link
Owner

In Web API you could have this operation i think:

public int Sum(int a, int? b = 5) {
     if (b == null)
           b = 10;
     return a + b;
}

I am not sure, but I think the following would apply with the TS client:

  • sum(1, 2) => 3
  • sum(1, null) => 11
  • sum(1) or sum(1, undefined) => 6

@jtheisen
Copy link
Author

jtheisen commented Sep 6, 2017

I see.

The problem I have with this is that if the model's "no there"-type is undefined, then frequently that's what I pass back at API calls again. If that doesn't match, I end up converting between undefined and null, which is annoying.

I think having an option to have both nullable and optional parameter values be reflected as undefined makes sense.

It technically excludes cases like the one you've given, but I for one wouldn't care.

@jtheisen
Copy link
Author

On additional related thing:

On setting nullValue to null rather than undefined, a nullable property gets defined as

SomeProp?: string | null;

This is undesirable as this means in fact undefined | null.

If nullValue is undefined, then the property definition is

SomeProp?: string | undefined;

Here, the ? is merely redundant (or the undefined is).

@RicoSuter
Copy link
Owner

I think theres also an option to not generate the ?, GenerateOptionalProperties?

@simeyla
Copy link

simeyla commented Jan 25, 2019

Seems the default value of Undefined is troublesome with strict mode.

I think the default should be made to be string | null for a string field.

You get this typescript generated with the default of Undefined: (I think this is still the default?)

export interface Address {
    firstName: string | undefined;
    lastName: string | undefined;

This is for this C# class:

public class Person
{
    [JsonProperty(Required = Required.AllowNull)]
    public string FirstName { get; set; }

    [JsonProperty(Required = Required.AllowNull)]
    public string LastName { get; set; }
}

The problem is that with strict mode you cannot assign null to string | undefined. I also don't want to use ?, and JSON.stringify() doesn't serialized fields with undefined assigned to them.

So I think the default should be null for sure.


I managed to switch to using nullValue: 'Null' in my nswag file, and that now gives me the correct type. So I'm fine with this, just suggesting changing the default.

"codeGenerators": {
"swaggerToTypeScriptClient": {
"className": "{controller}Client",
"moduleName": "API",
"namespace": "",
"typeScriptVersion": 2.7,
"template": "Angular",
"promiseType": "Promise",
"httpClass": "HttpClient",
"useSingletonProvider": true,
"injectionTokenType": "InjectionToken",
"rxJsVersion": 6.0,
"dateTimeType": "MomentJS",
"nullValue": "Null",

I have wondered if having a third option of Null | Undefined would make any sense?, but I can do that with Partial<Address> if I need to (which I may do if I use the generated entities elsewhere).

eg. You can do this, and create sub-models and always be sure the names match the generated code.:

 type UIAddress = Partial<Address>;
 type CityStateZip = Pick<Address, 'city' | 'state' | 'zip'>;

Typescript is amazing!

@RicoSuter
Copy link
Owner

Related: RicoSuter/NJsonSchema#882 (next version of nswag)

@simeyla
Copy link

simeyla commented Jun 10, 2021

@RicoSuter In case you weren't aware, the next version of typescript will be able to distinguish between undefined and an optional property (?) that is not present. You would not be able to assign undefined to an optional property unless that property had an explicit undefined in the clause.

With --strictOptionalProperties turned on this is now an error, where before it was not. This is currently available in the playground nightly build (need to set the option).

image

See also microsoft/TypeScript#13195 and microsoft/TypeScript#43947

Not sure of the exact consequences for nswag (if any), but thought you and others may be interested to see this.

@jeremyVignelles
Copy link
Collaborator

@simeyla : I don't think NSwag is impacted because it doesn't create object litterals, but instead serializes the function parameter.

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

No branches or pull requests

4 participants