glideapps/quicktype

[BUG]: (Dart) Null is better than empty array for null/undefined list-type properties

nakamura-work opened this issue · 0 comments

Issue Type

  • quicktype output

Context (Environment, Version, Language)

Input Format: JSON Schema
Output Language: Dart

CLI, npm, or app.quicktype.io: both
Version: 23.0.170

Description

In DartRenderer.ts#L233, null/undefined value of a list-type property will fall back to an empty array.
However, some APIs, including ours, distinguish between null and [].

e.g. following is the simplified pseudocode.

{
  "$ref": "#/definitions/API",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "API": {
      "properties": {
        "onlyFor": {
          "items": {
            "type": "string" 
          },
          "type": "array" 
        }
      },
      "type": "object" 
    }
  }
}
Coordinate(onlyFor: null).toJson(); // This will be `{onlyFor: []}`

This causes problems for the following logic:

type API = {
    onlyFor?: string[];
};

function isEligible(res: API) {
    const onlyFor = res.onlyFor;
    if (onlyFor == null) return true;
    return onlyFor.some((c) => c === process.platform);
}

Input Data

{
  "$ref": "#/definitions/API",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "API": {
      "properties": {
        "onlyFor": {
          "items": {
            "type": "string" 
          },
          "type": "array" 
        }
      },
      "type": "object" 
    }
  }
}

Expected Behaviour / Output

class Coordinate {
    List<String>? onlyFor;

    Coordinate({
        this.onlyFor,
    });

    factory Coordinate.fromJson(Map<String, dynamic> json) => Coordinate(
        onlyFor: json["onlyFor"] == null ? null : List<String>.from(json["onlyFor"]!.map((x) => x)),
    );

    Map<String, dynamic> toJson() => {
        "onlyFor": onlyFor == null ? null : List<dynamic>.from(onlyFor!.map((x) => x)),
    };
}

Current Behaviour / Output

class Coordinate {
    List<String>? onlyFor;

    Coordinate({
        this.onlyFor,
    });

    factory Coordinate.fromJson(Map<String, dynamic> json) => Coordinate(
        onlyFor: json["onlyFor"] == null ? [] : List<String>.from(json["onlyFor"]!.map((x) => x)),
    );

    Map<String, dynamic> toJson() => {
        "onlyFor": onlyFor == null ? [] : List<dynamic>.from(onlyFor!.map((x) => x)),
    };
}

Steps to Reproduce

  1. Open https://app.quicktype.io/
  2. Set Source type to JSON Schema
  3. Write Input Data as source
  4. Select Dart language in Options
  5. Confirm that Null Safety is turned on

Possible Solution

return [list, " == null ? [] : ", "List<", itemType, ">.from(", list, "!.map((x) => ", mapper, "))"];

-            return [list, " == null ? [] : ", "List<", itemType, ">.from(", list, "!.map((x) => ", mapper, "))"];
+            return [list, " == null ? null : ", "List<", itemType, ">.from(", list, "!.map((x) => ", mapper, "))"];

We understand that this solution may impact existing APIs.
So we want to explore if there is a better alternative.