fix(discriminator): nested, non-congruent unions #2323
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Note
This PR was written with the help of LLM code assist tools. Please let me know if that's against repo policy and I'll close it.
Allows discriminator to operate on nested unions, and on unions in which some members lack the discriminating field entirely.
Enable discriminators on unions where some members lack the discriminator field (non-congruent discriminators)
Background
This PR addresses a critical issue encountered when generating JSON schemas for the custom-elements-manifest project, specifically related to discriminated unions in TypeScript where not all union members contain the discriminator field.
Related Issue: custom-elements-manifest #123
Problem Statement
The custom-elements-manifest project defines a complex type hierarchy for representing web component metadata:
This represents a hierarchical discriminator pattern where:
kind
field distinguishes the base type categoriescustomElement
field distinguishes between regular and custom element variantsCustomElementMixinDeclaration
,CustomElementDeclaration
) have additional fields that others lackThe Core Issue
Previously, ts-json-schema-generator would fail with this error:
This occurred because:
MixinDeclaration
andCustomElementMixinDeclaration
sharekind: "mixin"
ClassDeclaration
andCustomElementDeclaration
sharekind: "class"
Why This Pattern Matters
The custom-elements-manifest schema needs to represent the reality of web component development:
kind: "class"
,kind: "mixin"
)kind: "class"
+customElement: true
)This hierarchical structure allows tools to:
customElement: true
kind: "class"
Solution
This PR implements support for non-congruent and hierarchical discriminated unions by:
1. Non-Congruent Discriminator Support
Handles unions where some members lack the discriminator field entirely:
2. Hierarchical Discriminator Logic
For types sharing discriminator values, generates sophisticated conditional schemas:
3. Smart Error Detection
Still properly fails for genuinely invalid discriminators:
Implementation Details
Key Changes
UnionTypeFormatter.ts
: ModifiedgetJsonSchemaDiscriminatorDefinition()
to:Conditional Schema Generation: Uses JSON Schema
if
/then
/else
with:not
) for types lacking secondary fieldsHeuristic-Based Detection: Uses type name patterns (e.g.,
CustomElement*
) to identify hierarchical relationshipsTest Coverage
Impact
Before (❌ Broken)
After (✅ Working)
> ts-json-schema-generator --path schema.d.ts --type Package ✓ Generated valid JSON Schema with hierarchical discriminator support
Validation
The fix has been validated against the actual custom-elements-manifest test suite:
This enables the custom-elements-manifest project to successfully generate JSON schemas for their complex type hierarchy, unblocking schema-based validation and tooling for the web components ecosystem.