8

I have an enum thats maps HTTP status to their code, as follow:

enter image description here

I can easily get those codes by accessing their keys (notice how IntelliSense shows HttpStatus.NOT_FOUND = 404 on the right panel)

enter image description here

Now let's say I have a function called sendStatus:

How should I type this function in order to get those codes autocompleted by IntelliSense?

enter image description here

Using keyof typeof doesn't work because it doesn't autocomplete the enum values.

BernaMariano
  • 846
  • 2
  • 9
  • 27
  • 1
    I just want to send the key as a string as argument (`sendStatus("NOT_FOUND"`), and not `sendStatus(HttpStatus.NOT_FOUND)` – BernaMariano Apr 14 '20 at 22:15

2 Answers2

13

What you want is not directly possible. When you do keyof typeof you create a union type 'CREATED' | 'OK' | 'NOT_FOUND' ... that is completely separate from the enum it once was.

The closest you can get is by doing

function sendStatus(code: HttpStatus) {
    // code
}

send(HttpStatus.OK) // This will autocomplete and show the status code numbers

and then converting the code to string inside the sendStatus function.

It's hard to say what you really want without knowing the exact usage you're looking for, but I would consider just having a plain old object instead of the enum

const HTTP_STATUS = {'OK':200, 'CREATED':201} as const

Then, if you need to, you can also create both of the types like so

type StringStatus = keyof typeof HTTP_STATUS // 'OK' | 'CREATED'
type NumsStatus = (typeof HTTP_STATUS)[keyof typeof HTTP_STATUS] // 200 | 201

Generally there's rarely a good reason to use enums in modern TS. Usually objects and/or union types do the job much better.

  • 1
    I upvoted your answer (I totally missed this in mine) because it is correct with regards to the diagnosis, but I disagree with the statement about Enums. If as in the OP's case there is value in having both a string and a numeric representation, Enums are ideal. What's more, I believe there is a performance advantage as Enums are passed around and compared as just integers, even though in the code you refer to its string name. Though perhaps in modern JS engines the difference is optimized away? – Inigo Apr 24 '20 at 17:54
  • Thanks for the response! So I'm not fighting a holy war against enums, there can be legitimate use cases. It's also a matter of personal preference, but I think most of the time you're better of without them. [This answer](https://stackoverflow.com/a/60041791/7595633) explains pretty much how I think about this. – SquattingSlavInTracksuit Apr 24 '20 at 18:11
  • 1
    btw re: `(typeof HTTP_STATUS)[keyof typeof HTTP_STATUS]` Where is this kind of Typescript expression documented? I can see its behavior... it's essentially a map operation. But I would never have know you could do this until I say your answer. – Inigo Apr 24 '20 at 19:04
  • https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types – SquattingSlavInTracksuit Apr 27 '20 at 08:01
  • I've read that before. It doesn't cover your usage, which is essentially `(type)[keys for that type] => values for that type as a union type`. In fact, no where on that page is even the combination `keyof typeof`! – Inigo Apr 27 '20 at 08:54
5

You can test keyof typeof by running example like this, which shows that Enum itself works fine in your case.

enum HttpStatus {
    OK = 200,
    CREATED = 201
}

function printStatus(code: keyof typeof HttpStatus) {
    const num = HttpStatus[code];
    if (num <= HttpStatus.CREATED) {
       console.log('HTTP Status key is: ', code);
       console.log('HTTP Status value is: ', num);
    }
}

printStatus("OK");

// Prints out
// > HTTP Status key is:  – "OK"
// > HTTP Status value is:  – 200
// Which shows that Enum value is being detected properly

keyof typeof gets you type that represents all Enum keys as string, and this is why autocompletion behaves like that. https://www.typescriptlang.org/docs/handbook/enums.html#enums-at-compile-time

shpasta
  • 1,913
  • 15
  • 21
  • Thank you for the reply! I just want the code completion to show me the enum values, nothing to do with the function body... I want to see `"BAD_REQUEST"......404` instead of `"BAD_REQUEST"......BAD_REQUEST`. Clearly `keyof typeof` isnt what I’m looking for... – BernaMariano Apr 15 '20 at 01:43
  • 1
    It's hard to tell without knowing more context of your needs, but if you want autocompletion to show the value, then I suggest using enums as actual enums, by their key. It would look like `sendStatus(HttpStatus.OK)`. `keyof typeof HttpStatus` returns type of '"OK" | "CREATED"', and that type doesn't know about number values of actual original Enum. – shpasta Apr 15 '20 at 17:46