Skip to content

Support type: null in anyOf/oneOf/allOf as a nullability marker - #933

Open
ThomasDutartre wants to merge 1 commit into
apple:mainfrom
ThomasDutartre:feat/support-nullables
Open

Support type: null in anyOf/oneOf/allOf as a nullability marker#933
ThomasDutartre wants to merge 1 commit into
apple:mainfrom
ThomasDutartre:feat/support-nullables

Conversation

@ThomasDutartre

@ThomasDutartre ThomasDutartre commented Aug 6, 2026

Copy link
Copy Markdown

Motivation

In OpenAPI 3.1, a nullable value is commonly expressed by adding a { "type": "null" } branch to an anyOf/oneOf/allOf (e.g. anyOf: [$ref, {type: null}]). Today the generator treats the null branch as an unsupported member, so the whole composition is skipped and emits Schema "null" is not supported ... skipping. This blocks common specs (FastAPI, Figma, and others) and is tracked in #906, #817, #419, #565, #513, #286.

OpenAPIKit already propagates the nullability onto the enclosing composition's core context when one of its subschemas is null, so the generator only needs to stop rejecting these schemas and skip the null member. This is the minimal, non-collapsing approach previously suggested by the maintainers, which keeps API evolution deterministic (adding a member expands the wrapper instead of changing its shape).

Prior maintainer guidance (the "filter" approach)

This PR intentionally implements the filtering approach that @czechboy0 described earlier, rather than the collapsing/optional-root approach of #557 and #558:

Minimal example that should be supported

{
  "components": {
    "schemas": {
      "Child": { "type": "string" },
      "Parent": {
        "type": "object",
        "properties": {
          "child": {
            "anyOf": [
              { "$ref": "#/components/schemas/Child" },
              { "type": "null" }
            ]
          }
        }
      }
    }
  }
}

Modifications

  • Added JSONSchema.isNullType helper.
  • isSchemaSupported: filter out the null branch of allOf/anyOf/oneOf before checking support, so the composition is generated instead of skipped.
  • translateAllAnyOneOf: skip the null branch when generating members/cases (no member/case is emitted for it).
  • translateArray: preserve the element's optionality ([Element?]), consistent with how builtin element types are already emitted, so arrays that mix values and null are supported.

Result

  • anyOf/oneOf/allOf containing a type: null branch are now generated as an optional wrapper (e.g. var b: A.bPayload?) instead of being skipped.
  • The wrapper is not collapsed: oneOf: [$ref, null] yields a single-case optional enum, and adding a case simply expands it — API evolution stays deterministic.
  • Arrays of nullable elements are emitted as [Element?], preserving order while allowing null entries.
  • Non-nullable compositions and arrays are unchanged.

Test Plan

  • Added snippet tests in SnippetBasedReferenceTests for oneOf/anyOf with null (single and multiple refs) and for an array with a nullable oneOf element.
  • swift test: 329 tests, 0 failures.
  • Validated end-to-end against a real-world spec: generation succeeds with no Schema "null" is not supported warnings, and nullable properties/array elements are emitted as optionals.

Treat a `type: null` branch inside anyOf/oneOf/allOf purely as a
nullability marker (already reflected on the composition's core context
by OpenAPIKit) instead of an unsupported member. Such compositions are
now generated as an optional wrapper without collapsing, keeping API
evolution deterministic.

Also preserve array element optionality so nullable elements are emitted
as `[Element?]`, consistent with builtin element types, allowing arrays
that mix values and nulls while preserving order.

Adds snippet tests for oneOf/anyOf with null (single and multiple refs)
and for arrays with a nullable oneOf element.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ThomasDutartre
ThomasDutartre marked this pull request as ready for review August 6, 2026 15:29
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

Successfully merging this pull request may close these issues.

1 participant