# OpenAPI: Enums

## Enum value naming

### Basic conversion

Enum values are named according to their values, with adjustments made to form valid identifiers:

- Invalid characters are removed.
- Values are converted to fit the case style of the target programming language.
- Special characters (for example, `+`, `-`, and `.`) are converted to words (like `Plus`, `Minus`, and `Dot`).

### Name conflicts

If naming conflicts arise after sanitization, deduplication is attempted by modifying case styles or adding suffixes.

For example, given the following schema:

```yaml
schema:
  type: string
  enum:
    - foo
    - Foo
    - FOO
```

Resulting enum values will be `FOO_LOWER`, `FOO_MIXED`, and `FOO_UPPER`.

If unique names cannot be resolved, a validation error will prompt you to resolve conflicts, potentially using the `x-speakeasy-enums` extension.

The recommended approach is to use the map format, which prevents length mismatch errors:

```yaml
schema:
  type: integer
  enum:
    - 1
    - 2
    - 3
  x-speakeasy-enums:
    1: NOT_STARTED
    2: IN_PROGRESS
    3: COMPLETE
```

Alternatively, you can use the array format, but ensure the order in the enum array corresponds exactly to the custom names in the `x-speakeasy-enums` array:

```yaml
schema:
  type: integer
  enum:
    - 1
    - 2
    - 3
  x-speakeasy-enums:
    - NOT_STARTED
    - IN_PROGRESS
    - COMPLETE
```

## Preserve custom enum names in TypeScript

When `x-speakeasy-enums` provides custom names, the TypeScript generator applies PascalCase transformation by default. This can mangle names where casing carries meaning. For example, `X86_64` becomes `X8664` because the transformation strips underscores and collapses digits.

To preserve custom enum names exactly as written, enable the [`fixEnumNameSanitization`](/docs/speakeasy-reference/generation/ts-config#error-and-response-handling) option in `gen.yaml`:

```yaml
typescript:
  fixEnumNameSanitization: true
```

With this option enabled, custom names provided via `x-speakeasy-enums` are kept as-is, with only illegal character sanitization applied. No PascalCase or other casing transformations are performed.

For example, given the following OpenAPI schema and `gen.yaml` configuration:

```yaml
# gen.yaml
typescript:
  fixEnumNameSanitization: true
```

```yaml
# OpenAPI spec
ImageArchitecture:
  enum: ["x86_64", "arm64"]
  type: string
  x-speakeasy-enums:
    x86_64: X86_64
```

The generated enum member is `X86_64` instead of `X8664`.

<Callout title="Note" type="info">
  The `fixEnumNameSanitization` option is currently available for TypeScript only (including `mcp-typescript`). It applies exclusively to enums with `x-speakeasy-enums` overrides — enums without custom names are unaffected.
</Callout>

Because this option skips all casing transformations for custom names, names are used exactly as provided. For example, a custom name like `under_review` remains `under_review` in the generated output rather than being converted to `UnderReview`.

## Enum class naming

Use the `x-speakeasy-name-override` attribute to customize enum class names:

```yaml
Enum:
  x-speakeasy-name-override: example_override
  type: string
  enum:
    - foo
    - FOO
```

This schema will produce:

```python
class ExampleOverride(str, Enum):
    FOO_LOWER = 'foo'
    FOO_UPPER = 'FOO'
```

### Name conflict considerations

Some cases (like open enums) may pose unique name resolutions challenges, particularly when similar names occur in the schema.

In name conflict cases, the parent schema is given the original name, while the child schema's name is concatenated with the parent's name.

For example, the following schema:

```yaml
enum_field:
  oneOf:
    - type: string
    - type: string
      enum:
        - foo
        - FOO
      x-speakeasy-name-override: enum_field
```

Results in:

```python
class EnumFieldEnumField(str, Enum):
  FOO_LOWER = 'value'
  FOO_UPPER = 'value'
```

To avoid naming conflicts, additional overrides may be necessary. For example:

```yaml
enum_field:
  x-speakeasy-name-override: enum_field_parent
          oneOf:
            - type: string
            - type: string
              enum:
                - foo
                - Foo
              x-speakeasy-name-override: enum_field
```

This will result in:

```python
class EnumField(str, Enum):
  FOO_LOWER = 'value'
  FOO_UPPER = 'value'
```

## Open vs closed enums

<Callout title="Note" type="info">
  This feature is currently supported in Go, Python, TypeScript, Java, C#,
  Ruby, and Terraform.
</Callout>

By default, enums defined in OpenAPI are considered closed during code
generation. This means that introducing a new member to an enum can become a
breaking change for consumers of older versions of the SDK. Sometimes, this is
desirable because particular enums can be rigidly defined and not changing in
the foreseeable future (country codes might be a good example of this).

However, in some cases, you might want to make room for future iteration and the
introduction of new enum members. This is where open enums can help. Use the
`x-speakeasy-unknown-values` extension to mark an enum as open:

```yaml
components:
  schemas:
    BackgroundColor:
      type: string
      x-speakeasy-unknown-values: allow
      enum:
        - red
        - green
        - blue
```

When an SDK is generated with this type, the API is able to send values beyond
what is specified in the enum and these will be captured and returned to the user
in a type-safe manner.

Here's how the `BackgroundColor` model translates to different languages:

<CodeWithTabs client:visible
  tabs={[
    {
      label: "TypeScript",
      language: "typescript",
      code: `type BackgroundColor = "red" | "green" | "blue" | Unrecognized<string>;`,
    },
    {
      label: "Go",
      language: "go",
      code: `type BackgroundColor string

  const (
      BackgroundColorRed   BackgroundColor = "red"
      BackgroundColorGreen BackgroundColor = "green"
      BackgroundColorBlue  BackgroundColor = "blue"
  )`,
    },
    {
      label: "Python",
      language: "python",
      code: `BackgroundColor = Union[Literal["red", "green", "blue"], UnrecognizedStr]`,
    },
    {
      label: "C#",
      language: "csharp",
      code: `// Known values mimic enum semantics
var green = BackgroundColor.GREEN;
green.IsKnown(); // true

// Use .Of() to create an open-enum instance from any string value
var purple = BackgroundColor.Of("purple");
purple.IsKnown(); // false

// Alternatively, the implicit operator can be used
BackgroundColor red = "red";
red.IsKnown(); // true

// Enum-like equality checks are also available
red.Equals(BackgroundColor.RED); // true
green == BackgroundColor.Of("green"); // true`,
    },
    {
      label: "Java",
      language: "java",
      code: `// Known values mimic enum semantics
BackgroundColor green = BackgroundColor.GREEN;

// Use .of() to create an open-enum instance from any string value
BackgroundColor purple = BackgroundColor.of("purple");
purple.isKnown(); // false

// Enum-like equality checks are also available
BackgroundColor.of("blue").equals(BackgroundColor.BLUE); // true
green == BackgroundColor.of("green"); // true`,
},
    {
      label: "Ruby",
      language: "ruby",
      code: `# Known values are defined as constants
green = Shared::BackgroundColor::GREEN
green.known?   # => true
green.serialize # => "green"

## Open enums accept unknown values via deserialize
purple = Shared::BackgroundColor.deserialize("purple")
purple.known?   # => false
purple.serialize # => "purple"

## Equality works across known and unknown values
Shared::BackgroundColor.deserialize("green") == Shared::BackgroundColor::GREEN # => true`,
}
]}
/>

## Native enums vs union of strings

Languages like Python and TypeScript support string or integer literal unions as
well as native enum types. When generating SDKs for these targets, specify
the preferred style using the `enumFormat` option in the
`.speakeasy/gen.yaml` config file where the SDK is generated.

For example, in the `gen.yaml` file:

```yaml
typescript:
  enumFormat: union
```

This will cause all enums to be generated as a union of strings:

```typescript
type Color = "sky-blue" | "dark-gray" | "stone";
```

```typescript

const sdk = new SDK();

const result = await sdk.themes.create({
  name: "flashy",
  background: "dark-gray",
});
```

Whereas this:

```yaml
typescript:
  enumFormat: enum
```

Will result in the following output:

```typescript
enum Color {
  SkyBlue = "sky-blue",
  DarkGray = "dark-gray",
  Stone = "stone",
}
```

```typescript

const sdk = new SDK();

const result = await sdk.themes.create({
  name: "flashy",
  background: Color.DarkGray,
});
```

The main trade-offs to consider between the two styles are that literal unions
do not require SDK users to import any additional types or values from the SDK
package. The user starts typing a string or number and their IDE's autocompletion
interface will suggest from the valid set of values.

Native enums need to be
imported from within the SDK but benefit from having members with clear names
and documentation on each. This is particularly useful when you define enums
that do not map well to spoken language, such as `enum: ["+", "_", "*", "!"]`.
Using the `x-speakeasy-enums` extension will allow you to customize the name of
each native enum member.

In TypeScript and Python, native enums are nominally typed, which means that
users cannot pass in any string value they have or another native enum that
overlaps with the desired one without triggering a type-checker error. In some
of these instances, they may need to write some code to map values to native
enum members.

### Mixing enum formats

While `enumFormat` is a global setting, it is possible to mix and match the enum
format on a per-schema basis using the `x-speakeasy-enum-format` extension:

```yaml
# `enumFormat` is set to `union` in the gen.yaml
components:
  schemas:
    BackgroundColor:
      type: int
      x-speakeasy-enum-format: enum
      enum:
        - 1
        - 2
        - 3
      x-speakeasy-enums:
        1: Red
        2: Green
        3: Blue
```

In this case, the `BackgroundColor` enum will be generated as a native enum in
the target language, while the rest of the enums will be generated as a union of
values.

## Enum value descriptions

Use the `x-speakeasy-enum-descriptions` extension to add descriptions to enum values that will appear as documentation in the generated SDK code. This is particularly useful for providing context about each enum option to SDK users through IDE hints and generated documentation.

The extension supports two formats:

### Array format

Provide descriptions as an array that corresponds to the enum values in order:

```yaml
components:
  schemas:
    ProgrammingLanguage:
      description: Programming language environment for the generated code.
      type: string
      x-speakeasy-enum-descriptions:
        - Python >= 3.10, with numpy and simpy available.
        - TypeScript >= 4.0
      enum:
        - PYTHON
        - TYPESCRIPT
```

A lint rule ensures that the `x-speakeasy-enum-descriptions` array must be the same length as the `enum` array.

### Map format

Alternatively, use a map format where keys are enum values:

```yaml
components:
  schemas:
    ProgrammingLanguage:
      description: Programming language environment for the generated code.
      type: string
      x-speakeasy-enum-descriptions:
        PYTHON: Python >= 3.10, with numpy and simpy available.
        TYPESCRIPT: TypeScript >= 4.0
      enum:
        - PYTHON
        - TYPESCRIPT
```

<Callout title="Note" type="info">
The map format is the preferred method when using overlays. If a new enum member is added to the OpenAPI specification that isn't in the
`x-speakeasy-enum-descriptions` map, it will only cause a lint warning rather than a lint failure during generation.
</Callout>

Both formats generate the same output with JSDoc comments:

```typescript
/**
 * Programming language environment for the generated code.
 */
export enum ProgrammingLanguage {
  /**
   * Python >= 3.10, with numpy and simpy available.
   */
  PYTHON = "PYTHON",

  /**
   * TypeScript >= 4.0
   */
  TYPESCRIPT = "TYPESCRIPT",
}
```

<Callout title="Note" type="info">
This feature works primarily with `enumFormat: enum`. When using native enum types, the descriptions will be rendered as JSDoc comments that provide IDE hints and appear in generated documentation. When rendered into literal unions, whilst the description is available above the literal value in source-code, our testing implied most language servers / IDEs will not automatically present it to the user.
</Callout>
