timostamm / protobuf-ts

Protobuf and RPC for TypeScript
Apache License 2.0
1.1k stars 131 forks source link

Typesafe API to retrieve enums from strings #642

Open dimo414 opened 7 months ago

dimo414 commented 7 months ago

To my knowledge, TypeScript's support for going from a string to an enum requires that the string be typed as keyof typeof MyEnum. This works for string literals like the example but doesn't work for arbitrary strings which could be invalid values. Here's a playground demo which reports an Element implicitly has an 'any' type error.

As a more concrete example, I am receiving a string from an untyped client that "should" be an enum value. To satisfy the type checker I need to cast the key as the expected type, and then handle the potential undefined that is returned on a miss to convert it to the enum's 0th value:

let key = "MAYBE";
// notice the type here is "wrong" because the index lookup returns undefined,
// but I don't want to pollute myEnum with |undefined, I want it to use the default
let myEnum: MyEnum = MyEnum[key as keyof typeof MyEnum];
if (myEnum === undefined) {
  myEnum = MyEnum.ANY;
}

It would be really useful if protobuf-ts provided a more ergonomic string-to-enum utility than what TypeScript provides. At a minimum it should support arbitrary strings without needing casting, and potentially it could behave like the protobuf spec and return the 0th enum value rather than undefined.

jcready commented 7 months ago

You should be able to create these functions yourself (playground):

function isKeyof<T extends {}>(t: T, key: string): key is string & keyof T {
    return key in t;
}

function getEnumValueOrDefault<T extends {}>(e: T, key: string): 0 | T[string & keyof T] {
    return isKeyof(e, key) ? e[key] : 0;
}
dimo414 commented 7 months ago

Yeah I agree it's possible to workaround, but I was hoping protobuf-ts could include these e.g. in the generated code of each enum so it's right on the type, rather than something we need to pull together or go find.

jcready commented 7 months ago

The enums are just TS enums. No extra properties or methods can be added and have them be TS enums.

dimo414 commented 7 months ago

Fair enough; well IMO there's room for protobuf-ts to improve the ergonomics of string->enum lookups vs. what TypeScript provides out of the box, but I understand if you disagree.