Skip to content

feat(type-helpers): add reusable union schema factory - #4025

Open
dogemad wants to merge 1 commit into
nestjs:v12.0.0from
dogemad:feat/union-type-helper
Open

feat(type-helpers): add reusable union schema factory#4025
dogemad wants to merge 1 commit into
nestjs:v12.0.0from
dogemad:feat/union-type-helper

Conversation

@dogemad

@dogemad dogemad commented Jul 24, 2026

Copy link
Copy Markdown

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Other... Please describe:

What is the current behavior?

Reusable OpenAPI union schemas require users to manually define oneOf
references, register every referenced DTO with ApiExtraModels, and maintain a
separate TypeScript union type.

The OpenAPI schema and application type can therefore drift apart. In addition,
generateSchema() looks up its result using the runtime class name, which does
not work correctly for schemas with a custom component name.

Issue Number: Fixes #3886

What is the new behavior?

This PR introduces createUnionApiSchema(), which creates a reusable named
OpenAPI oneOf component and preserves the represented TypeScript union.

const Pet = createUnionApiSchema({
  name: 'Pet',
  oneOf: [CatDto, DogDto],
  discriminator: {
    propertyName: 'type',
    mapping: {
      cat: CatDto,
      dog: DogDto
    }
  }
});

type Pet = InferUnionApiSchema<typeof Pet>;

The new helper:

  • infers the application union from its members
  • registers referenced DTOs automatically
  • supports concrete and abstract DTO classes
  • supports Swagger built-in schema constructors
  • supports discriminator mappings and descriptions
  • supports custom DTO component names
  • supports nested generated union schemas
  • rejects empty unions and invalid Function members
  • prevents members from using the same component name as the union
  • isolates stored metadata from generated document mutations

generateSchema() now uses the component name returned by schema exploration,
so it also resolves schemas decorated with a custom ApiSchema name correctly.

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

The generated value is a Swagger metadata host rather than a constructor for a
union member. InferUnionApiSchema<typeof Schema> exposes the corresponding
application type without claiming that new Schema() creates one of its DTOs.

Validation performed:

  • npm run build
  • npm run lint
  • npm test — 415 tests passed
  • npm run test:e2e — 127 tests passed
  • Prettier check for all changed files

Add createUnionApiSchema() to emit named oneOf components while preserving
the corresponding TypeScript union through InferUnionApiSchema.

Register referenced DTOs automatically and support built-in schema constructors,
discriminators, custom component names, and nested union tokens.

Update schema exploration and generateSchema() so generated tokens resolve
through their declared component names.
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