Skip to content

Remove | string from enums #4278

Closed
@everett1992

Description

@everett1992

Describe the feature

smithy-typescript and aws-sdk-js-v3 generate clients with | string for any enum type in the model. It breaks auto complete, which is annoying, and compile time type checks, which is unsafe.

Use Case

TypeScript simplifies the documented 1 type from ExecutionStatus | string to string in error messages, auto complete, and type hints so it's hard to discover the generated enum values from an editor or commandline.

The inclusion of string in output types prevents me from writing exhaustiveness checks that would fail at compile time if my code did not handle all cases. For example handling step functions execution status1.

// According to aws-sdk docs[^1] the type is ExecutionStatus | string | undefined
// but according to typescript the type is `string | undefined`.
//  ExecutionStatus | string is simplified to `string`.
const {status} = await sfn.describeExecution({executionArn})

switch (status) {
  case ExecutionStatus.ABORTED:
  case ExecutionStatus.FAILED:
  case ExecutionStatus.TIMED_OUT:
    return false;
  case ExecutionStatus.RUNNING:
    return undefined;
  case ExecutionStatus.SUCCEEDED:
    return true;
    console.log(`some known status ${status}`)
  case undefined:
    // https://github.com/aws/aws-sdk-js-v3/issues/1613
    throw Error('undefined')
  default:
    // This is a type error because status could be any string other than the enumerated values we checked above.
    assertNever(status)        Argument of type 'string' is not assignable to parameter of type 'never'.
}

The inclusion of string in input types allows me to pass invalid values2

await sfn.createStateMachine({
  // no type error here :(
  type: 'nope',
  ...args
})

I understand that smithy requires3 clients to allow unmodeled enum values but it does not require the current implementation.

Proposed Solution

My proposal is to replace all | string with a specific boxed value Unmodeled<string>. This is inspired by rust aws-sdk4, but obviously typescript doesn't support parameterized enums like they do.

Instead of status: ExecutionStatus | string (which is simplified to string) we would have status: ExecutionStatus | Unmodeled<string>. aws-sdk can ship a isModeled typeguard which can narrow to only known values, or handle all unknown values.

// just one way to implement the box
const unmodeled = Symobol.for('unmodeled')
interface Unmodeled<T> { value: T, [unmodeled]: true }

function isModeled<T>(value: T | Unmodeled<unknown>) value is T {
  return !(unmodeled in value);
}

The sdk would still handle unknown enum values in respondes, but any value not modeled at build time would be wrapped with Unmodeled. Callers could still provide unmodeled values as long as they wrap the value, providing type safty for the majority of uses.

await sfn.createStateMachine({
  // no type error here :)
  type: Unmodeled('nope'),
  ...args
})

I think this pattern could be used beyond enums and replace | undefined and ? added to inputs. I do not recommend using this pattern for optional outputs #1613, where I prefer optional chaining.

Acknowledgements

  • I may be able to implement this feature request
  • This feature might incur a breaking change

SDK version used

3.229.0

Environment details (OS name and version, etc.)

N/A

Footnotes

  1. https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-sfn/interfaces/describeexecutioncommandoutput.html#status
    https://github.com/aws/aws-sdk-js-v3/blob/main/codegen/sdk-codegen/aws-models/sfn.json#L3479 2

  2. https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-sfn/interfaces/createstatemachinecommandinput.html#type
    https://github.com/aws/aws-sdk-js-v3/blob/main/codegen/sdk-codegen/aws-models/sfn.json#L5890

  3. Enums are considered open, meaning it is a backward compatible change to add new members. Previously generated clients MUST NOT fail when they encounter an unknown enum value. Client implementations MUST provide the capability of sending and receiving unknown enum values.
    https://smithy.io/2.0/spec/simple-types.html#enum-is-a-specialization-of-string

  4. https://docs.rs/aws-sdk-sfn/latest/aws_sdk_sfn/model/enum.ExecutionStatus.html

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature-requestNew feature or enhancement. May require GitHub community feedback.p2This is a standard priority issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions