Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Normalize construction of user facing enums #1534

Merged
merged 4 commits into from
Nov 14, 2024

Conversation

mjameswh
Copy link
Contributor

@mjameswh mjameswh commented Sep 25, 2024

What was changed

  • This PR normalizes conventions around user facing enums in the TS SDK.

  • More specifically, it features the following conventions:

    • No more redundant prefixes on enum values.

      For example:

        await startChild(sleep, {
      -   parentClosePolicy: ParentClosePolicy.PARENT_CLOSE_POLICY_ABANDON,
      +   parentClosePolicy: ParentClosePolicy.ABANDON,
        });
    • No more 'UNSPECIFIED' values in user facing enums; TypeScript's undefined already means that.

    • "Enum values" are now of type string, and the "enum" themselves are now implemented using const objects, with a string union type defined from all values of the const object. That makes it possible for users to simply pass the string representation of an enum value, without having to import and refer to the enum definition object. That's much more TS idiomatic and is better supported by TS code editors.

      The preceding example can therefore be further reduced down to:

        await startChild(sleep, {
      -   parentClosePolicy: ParentClosePolicy.ABANDON,
      +   parentClosePolicy: 'ABANDON',
        });
    • The const object is preserved (i.e not completely replaced by the union of strings type), for backward compatibility reasons, but also because it plays better in regard to documentation of each enum values.

    • We preserve build-time backward compatibility for enum values that existed before.

      For example, the following code will still compile, but will report a deprecation on ParentClosePolicy.PARENT_CLOSE_POLICY_ABANDON.

        await startChild(sleep, {
          parentClosePolicy: ParentClosePolicy.PARENT_CLOSE_POLICY_ABANDON,
        });
    • We maintain the SCREAMING_SNAKE_CASE format for enum values

    • For "user facing enums" to/from "proto enums" conversions, we normalize on a encodeEnumName(string): ProtoEnumValue and a decodeEnumName(ProtoEnumValue): string functions, where ProtoEnumValue really is the numerical value of the proto enum value. This ensures uniformity for all enum types, without risk of getting the proto definitions imported in the Workflow sandbox (which would significantly increase the per-workflow memory footprint).

@mjameswh mjameswh force-pushed the workflowidreusepolicy-enum branch from 744d42e to 855baab Compare November 12, 2024 20:22
@mjameswh mjameswh changed the title [RFC] Normalize structures around enums Normalize construction of user facing enums Nov 13, 2024
@mjameswh mjameswh marked this pull request as ready for review November 13, 2024 13:32
@mjameswh mjameswh requested a review from a team as a code owner November 13, 2024 13:32
@mjameswh mjameswh force-pushed the workflowidreusepolicy-enum branch from 855baab to 337ed11 Compare November 13, 2024 15:47
Copy link
Member

@Sushisource Sushisource left a comment

Choose a reason for hiding this comment

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

Cool! This should be a big QoL improvement

Copy link
Contributor

@dandavison dandavison left a comment

Choose a reason for hiding this comment

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

Wow! There's quite a lot going on... but this should be a really nice improvement.

I'll just ask one question for now. If we take something like

decodeStartChildWorkflowExecutionFailedCause(activation.failed.cause)

It would be nice if this looked more like

decode(activation.failed.cause)

Do you see any possibilities in that direction?

(
input: ShortStringEnumKey | `${Prefix}${ShortStringEnumKey}` | ProtoEnumValue | null | undefined
) => ProtoEnumValue | undefined, //
(input: ProtoEnumValue | null | undefined) => ShortStringEnumKey | undefined, //
Copy link
Contributor

Choose a reason for hiding this comment

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

When does null occur?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The protobuf-to-ts transpiler makes every field MyProtoType | null | undefined. That's just the way it is, unfortunately, so decode(...) must accept that.

Now, I don't think there's any legitimate use case for encode(...) to receive null as input, but just for symetry, most of our encode functions have followed that pattern anyway.

@mjameswh
Copy link
Contributor Author

Wow! There's quite a lot going on... but this should be a really nice improvement.

I'll just ask one question for now. If we take something like

decodeStartChildWorkflowExecutionFailedCause(activation.failed.cause)

It would be nice if this looked more like

decode(activation.failed.cause)

Do you see any possibilities in that direction?

You would need to pass something that represents the enum-const-object itself, e.g. decode(StartChildWorkflowExecutionFailedCause, activation.failed.cause). Same on encode().

I think that might be possible, by adding a symbol property on the enum-const-object to record the mapping and reverse-mapping tables.

I wouldn't dig into that possibility at this point though.

@mjameswh mjameswh enabled auto-merge (squash) November 14, 2024 21:31
@mjameswh mjameswh merged commit 9a9c0a9 into temporalio:main Nov 14, 2024
70 checks passed
lukeramsden pushed a commit to lukeramsden/sdk-typescript that referenced this pull request Nov 15, 2024
@mjameswh mjameswh deleted the workflowidreusepolicy-enum branch November 21, 2024 23:25
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.

3 participants