Skip to content

fix(plugin): support boolean literal types and boolean enum values#3761

Open
lucreiss wants to merge 1 commit intonestjs:masterfrom
lucreiss:fix/boolean-literal-enum-type-handling
Open

fix(plugin): support boolean literal types and boolean enum values#3761
lucreiss wants to merge 1 commit intonestjs:masterfrom
lucreiss:fix/boolean-literal-enum-type-handling

Conversation

@lucreiss
Copy link

@lucreiss lucreiss commented Mar 5, 2026

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?

  1. Boolean literal types are not recognized by the CLI plugin. When a DTO property is typed as literal true or false (e.g. hasPaymentMethod: true), the plugin's isBoolean() only checks TypeFlags.Boolean and misses TypeFlags.BooleanLiteral. This causes getTypeReferenceAsString to return { typeName: undefined }, so no type is emitted in the plugin metadata for the property.

  2. getEnumType() returns 'number' for boolean enum values. The function only distinguishes between 'string' and 'number', defaulting to 'number' for anything that isn't a string. So @ApiProperty({ enum: [true, false] }) produces type: "number" in the OpenAPI schema, which violates the OpenAPI 3.0 specification (Section 5.1 — Schema Object lists boolean as a valid primitive type).

  3. Explicit type is overwritten by enum type inference. Both createFromObjectLiteral() in SchemaObjectFactory and the @ApiProperty decorator unconditionally overwrite the type property with the result of getEnumType() when enum is present. This means @ApiProperty({ type: 'boolean', enum: [true] }) has its type: 'boolean' replaced with type: 'number', with no escape hatch for the user.

Issue Number: N/A

What is the new behavior?

  1. isBoolean() now also matches TypeFlags.BooleanLiteral, so the plugin correctly emits type: () => Boolean for literal true/false properties. A new isBooleanLiteral() helper is added, consistent with the existing isStringLiteral() and isEnumLiteral() functions.

  2. getEnumType() detects boolean values and returns 'boolean', producing spec-compliant OpenAPI schemas. SwaggerEnumType and getEnumValues() return types are updated to include boolean[].

  3. The enum type inference in both createFromObjectLiteral() and the @ApiProperty decorator now only applies when no explicit type is already set on the metadata. This is consistent with every other code path in the library where user-provided decorator metadata takes precedence over inference.

OpenAPI / JSON Schema specification compliance

These changes bring the generated schemas in line with the OpenAPI 3.0.3 specification and the JSON Schema Validation draft it builds on:

  • type: "boolean" is a first-class primitive. OAS 3.0.3 §4.7.24.1 (Schema Object) defines data types via JSON Schema §4.2, which lists boolean alongside string, number, integer, array, and object as valid primitive types. Before this fix, enum: [true, false] produced type: "number" — an invalid schema that would cause spec validators (e.g. swagger-cli validate, Redocly, Spectral) to flag a type mismatch since the enum values are not numbers.

  • enum constrains values, not types. JSON Schema §5.20 defines enum as a value-space constraint that is valid for any type. The OpenAPI toolchain (Swagger UI, Swagger Codegen, openapi-generator) resolves type independently from enum — they are orthogonal. By returning 'boolean' from getEnumType() when the values are booleans, the generated schema now correctly pairs type: "boolean" with enum: [true] or enum: [true, false], which is valid per §5.20 and renders correctly in Swagger UI as a constrained boolean field.

  • Explicit type must not be silently discarded. OAS 3.0.3 §4.7.24.1 states that the type field determines the data type of the schema. When a user explicitly declares @ApiProperty({ type: 'boolean', enum: [true] }), the intent is unambiguous — the property is a boolean constrained to a single value. Overwriting this to type: "number" produced a schema where the declared type contradicts the enum values, which is a validation error under JSON Schema §5.20 (enum values must be valid against the schema). The guard now preserves user intent and produces schemas where type and enum are always consistent.

  • Boolean literal discriminators are now representable. A common OpenAPI pattern for discriminated unions uses a fixed boolean property (e.g. hasSomething: true vs hasSomething: false) as a discriminator with oneOf. This requires { type: "boolean", enum: [true] } and { type: "boolean", enum: [false] } in the respective sub-schemas. Before this fix, both would incorrectly produce type: "number", breaking code generators (openapi-generator, swagger-codegen) that rely on type to determine the target language type for the property.

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Files changed (source):

  • lib/plugin/utils/ast-utils.ts — update isBoolean(), add isBooleanLiteral()
  • lib/utils/enum.utils.ts — add boolean support to getEnumType() and getEnumValues()
  • lib/types/swagger-enum.type.ts — add boolean[] to SwaggerEnumType
  • lib/services/schema-object-factory.ts — guard type assignment in createFromObjectLiteral()
  • lib/decorators/api-property.decorator.ts — guard type assignment in createApiPropertyDecorator()

Files changed (tests):

  • test/plugin/fixtures/boolean-literal.dto.ts — new fixture with boolean literal properties
  • test/plugin/model-class-visitor.spec.ts — test that plugin transpiles boolean literals to type: () => Boolean
  • test/utils/enum-utils.spec.ts — unit tests for getEnumType() and getEnumValues() with boolean values
  • test/services/schema-object-factory.spec.ts — tests for boolean enum schemas and explicit type preservation

The CLI plugin does not recognize TypeScript `BooleanLiteral` types (true/false),
causing properties typed as literal `true` or `false` to emit no `type` in metadata.
Additionally, `getEnumType()` only returns 'string' or 'number', so `enum: [true]`
or `enum: [true, false]` incorrectly produces `type: "number"` in the OpenAPI schema,
violating the spec. Finally, both `createFromObjectLiteral()` and the `@ApiProperty`
decorator unconditionally overwrite an explicit `type` with the inferred enum type,
preventing users from setting `type: 'boolean'` alongside `enum`.

- Update `isBoolean()` in ast-utils to also match `TypeFlags.BooleanLiteral`
- Add `isBooleanLiteral()` helper consistent with existing `isStringLiteral()`
- Add boolean detection to `getEnumType()` so it returns 'boolean' for boolean enums
- Include `boolean[]` in `SwaggerEnumType` and `getEnumValues()` return type
- Guard `type` assignment in `createFromObjectLiteral()` and `@ApiProperty` decorator
  to only infer from `getEnumType()` when no explicit `type` is already set
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