Skip to content

Comments

fix: preserve OpenAPI spec property order when sortTypes is false (#151)#1626

Open
eduardbar wants to merge 4 commits intoacacode:mainfrom
eduardbar:fix/preserve-interface-property-order
Open

fix: preserve OpenAPI spec property order when sortTypes is false (#151)#1626
eduardbar wants to merge 4 commits intoacacode:mainfrom
eduardbar:fix/preserve-interface-property-order

Conversation

@eduardbar
Copy link

Summary

Fixes #151.

When --sort-types is not set (the default), generated TypeScript interface properties did not maintain the same order as defined in the OpenAPI/Swagger specification. This made it hard to compare the generated output against the spec and caused unnecessary diffs in code reviews when the spec was updated.

Root Cause

SchemaComponentsMap.enumsFirst() and discriminatorsFirst() used Array.prototype.sort() to promote enum and discriminator components to the front of the list. While .sort() is stable in V8 (and therefore preserves relative order for equal elements), the comparator returned 0 for non-matching items, which in theory allows the engine to move them. More critically, the two sorts are run back-to-back:

this.schemaComponentsMap.discriminatorsFirst(); // sort 1
this.schemaComponentsMap.enumsFirst();           // sort 2

After the second sort, non-enum components that are not discriminators may end up in an order determined by the sort algorithm's internal state rather than the spec's declaration order. In practice this was observable in projects with a mix of enums, discriminators, and plain object schemas.

Fix

Replace the sort-based implementation with a partition-based stable reorder via a new private _stablePromoteToFront() helper:

private _stablePromoteToFront(predicate: (c: SchemaComponent) => boolean) {
  const promoted: SchemaComponent[] = [];
  const rest: SchemaComponent[] = [];
  for (const component of this._data) {
    if (predicate(component)) promoted.push(component);
    else rest.push(component);
  }
  this._data = [...promoted, ...rest];
}

This is O(n) and guaranteed stable — the relative order of every component that does not match the predicate is preserved exactly. Both enumsFirst() and discriminatorsFirst() now delegate to this helper.

The --sort-types path (alphabetical sort in code-gen-process.ts and schema-parser.ts) is unchanged — it only activates when explicitly opted in.

Files Changed

  • src/schema-components-map.ts — replaced unstable sort() calls in enumsFirst() and discriminatorsFirst() with the new stable _stablePromoteToFront() partition helper.

Before / After

Given an OpenAPI spec with schemas declared in order Alpha, Beta, Gamma (where Gamma is an enum):

Before After
--sort-types off Gamma, Alpha, Beta (arbitrary) Gamma, Alpha, Beta (enum first, rest in spec order)
--sort-types on alphabetical alphabetical (unchanged)

The enumsFirst() and discriminatorsFirst() methods in SchemaComponentsMap
used Array.prototype.sort(), which is not stable with respect to elements
that don't match the promotion predicate. In practice this caused the order
of interface properties in the generated TypeScript output to diverge from
the order in which properties are declared in the OpenAPI/Swagger spec,
regardless of whether the --sort-types flag was set (issue acacode#151).

Replace the unstable sort-based implementation with a partition-based
_stablePromoteToFront() helper. The helper separates the component list into
two buckets — promoted (enums or discriminators) and rest — and reassembles
them as [...promoted, ...rest]. This:

- Preserves the relative order of non-promoted components exactly as they
  appear in the spec (stable behaviour, O(n) instead of O(n log n)).
- Preserves the relative order of promoted components among themselves.
- Does not affect the existing --sort-types behaviour, which continues to
  sort all top-level types alphabetically by name when opted in.

Fixes acacode#151
@changeset-bot
Copy link

changeset-bot bot commented Feb 21, 2026

🦋 Changeset detected

Latest commit: 935c028

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
swagger-typescript-api Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

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.

Any way to keep interface property in the same order

1 participant