Skip to content

Add option to generate never for not schemas that accept everything - #1844

Merged
js2me merged 3 commits into
acacode:mainfrom
TheeCoderAhmed:fix/not-empty-schema-never
Nov 4, 2026
Merged

js2me merged 3 commits into
acacode:mainfrom
TheeCoderAhmed:fix/not-empty-schema-never

Conversation

@TheeCoderAhmed

@TheeCoderAhmed TheeCoderAhmed commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1821

not: {} generates any. No value can satisfy that schema, so never describes it better. With the schema from the issue, a union that allows exactly one of two properties comes out with externalId?: any and internalId?: any, so TypeScript accepts objects that set both.

Changing that output can break existing code, so after review it's now behind a new option: emptyNotAsNever in generateApi, --empty-not-as-never on the CLI. It is off by default and generated output doesn't change unless you turn it on.

With it on, not: {} and not: true generate never. true is not in the issue, but it accepts every value just like {}. Any other not still generates any, including annotation-only schemas such as not: { description: "..." }. The changeset is now a minor bump.

What I ran, on Node 22:

  • tests/spec/not-empty-schema has two tests. With the old parser, the option-on test fails at expect(content).toContain("externalId?: never;"). The default-off test passes with both.
  • vitest run: 306 tests in 101 files pass, with no snapshot changes.
  • biome check on the changed files reports one error, organizeImports at types/index.ts:1:1. This PR doesn't touch those lines.

What I did not run: bun run build, or Node 20, 24 and 25.


Summary by cubic

Fixes #1821 by adding an opt-in emptyNotAsNever option (--empty-not-as-never) that generates never instead of any for not: {} and not: true schemas, which accept every value and so can never be satisfied. Previously NotSchemaParser always returned any, letting TypeScript accept objects in the "exactly one of these properties" pattern that set both. The option is off by default, so existing generated output is unchanged unless it's enabled. Other not schemas still generate any because TypeScript cannot express them. Adds a changeset and a spec test.

Written for commit ca0f680. Summary will update on new commits.

View guided diff

NotSchemaParser returned any for every not schema, so not: {} and
not: true came out as any even though no value can satisfy them. The
common "exactly one of these properties" pattern lost its meaning.

Return never for those two forms. Other not schemas still generate any
because TypeScript cannot express them.

Fixes acacode#1821
@changeset-bot

changeset-bot Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ca0f680

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 Minor

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

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 4 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/schema-parser/complex-schema-parsers/not.ts">

<violation number="1" location="src/schema-parser/complex-schema-parsers/not.ts:8">
P2: This exact-equality check misses annotation-only schemas such as `not: { description: "..." }`. Annotations impose no validation constraints, so their negation is also impossible, yet this returns `any` and allows invalid values; detect schemas with no validation keywords rather than only `{}`.</violation>
</file>

Reply to a comment to ask cubic a question or push back. It learns from your replies.

View guided diff | Re-trigger cubic

return this.config.Ts.Keyword.Any;
// `{}` and `true` accept every value, so negating them leaves no valid value.
const allowsNothing =
this.schema.not === true || isEqual(this.schema.not, {});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This exact-equality check misses annotation-only schemas such as not: { description: "..." }. Annotations impose no validation constraints, so their negation is also impossible, yet this returns any and allows invalid values; detect schemas with no validation keywords rather than only {}.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/schema-parser/complex-schema-parsers/not.ts, line 8:

<comment>This exact-equality check misses annotation-only schemas such as `not: { description: "..." }`. Annotations impose no validation constraints, so their negation is also impossible, yet this returns `any` and allows invalid values; detect schemas with no validation keywords rather than only `{}`.</comment>

<file context>
@@ -1,7 +1,14 @@
-    return this.config.Ts.Keyword.Any;
+    // `{}` and `true` accept every value, so negating them leaves no valid value.
+    const allowsNothing =
+      this.schema.not === true || isEqual(this.schema.not, {});
+
+    return allowsNothing
</file context>

Comment thread tests/spec/not-empty-schema/schema.json Resolved
Changing the generated type from any to never can break existing code, so
the new output is now opt-in through a generate param and a CLI flag.
@TheeCoderAhmed TheeCoderAhmed changed the title Generate never for not schemas that accept everything Add option to generate never for not schemas that accept everything Nov 4, 2026
@js2me

js2me commented Nov 4, 2026

Copy link
Copy Markdown
Member

Thank you!

@js2me
js2me merged commit ad7b7ef into acacode:main Nov 4, 2026
5 checks passed
@github-actions github-actions Bot mentioned this pull request Nov 4, 2026
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.

not: {} generates any instead of never

2 participants