Skip to content

Boolean literal discriminant collapses to single value in tagged unions #1841

@wildduck2

Description

@wildduck2

What happens

When a discriminated union uses a boolean literal as the tag (ok: true | false), the generated SDK output emits ok: true on both branches. The false literal is lost.

Versions

  • typia: 11.0.3
  • @nestia/sdk: 10.0.2
  • nestia: 10.0.2
  • typescript: 5.9.3

Repro

Source controller return type:

| { ok: false; error: { code: string } }
| { ok: true; code: string; data: { id: string } }

Generated SDK output:

| { ok: true; error: { code: string } }   // wrong, should be ok: false
| { ok: true; code: string; data: { id: string } }

Both branches end up with ok: true. Client-side narrowing via if (res.ok) is broken because both branches are now structurally compatible on the discriminator.

Minimal sample

NestJS controller:

@TypedRoute.Get('me')
async getMe(): Promise<
  | { ok: false; error: { code: 'X' } }
  | { ok: true; code: 'X'; data: number }
> {
  return { ok: true, code: 'X', data: 1 }
}

Run nestia sdk. Inspect generated me/index.ts. Both Output union members will have ok: true.

Workaround

String tag survives. Replace boolean discriminator with string:

| { kind: 'error'; ok: false; ... }
| { kind: 'success'; ok: true; ... }

kind round-trips fine. Only ok: true | false collapses.

Or use property existence on the client ('data' in res) instead of res.ok.

Where I think the bug lives

I have not traced it all the way, but the symptom looks like boolean literal information being dropped during metadata flattening. String literals in the same position keep both members. So the issue seems specific to how boolean atomics are stored or re-emitted.

Happy to send a PR if someone can point at the right file.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions