3

I have a const enum in TypeScript, something like below:

const enum ControlId {
   ZoomIn = 600,
   ZoomOut,
   Close, 
   Open,
   ....
}

Given a number, I want to know if it is within the range of ControlId, which is [600, ?], so that I can know if it falls into the category of ControlId or not. I am wondering if there is a way to do that. Thanks.

flyingbee
  • 601
  • 1
  • 7
  • 17
  • To be clear, you have a **runtime** value, and you want to see if it's a valid `Controlid`? – T.J. Crowder May 18 '22 at 07:12
  • 1
    You are unable to get an array of values from `ControlId` because it is `const enum`. Please see related [answer](https://stackoverflow.com/questions/72269433/type-for-object-keyssomeenum-in-typescript/72270293#72270293) – captain-yossarian from Ukraine May 18 '22 at 07:13
  • 2
    Not with `const enum` because it doesn't exist at runtime (replaced by numbers at compile time). It is possible with "regular" enum – Aleksey L. May 18 '22 at 07:13
  • `Math.min/max(Object.values(ControlId))` should do – Bergi May 18 '22 at 07:14
  • https://www.typescriptlang.org/docs/handbook/enums.html https://stackoverflow.com/a/51536142/495157 – JGFMK May 18 '22 at 07:15
  • 1
    Does this answer your question? [TypeScript enum to object array](https://stackoverflow.com/questions/43100718/typescript-enum-to-object-array) – JGFMK May 18 '22 at 07:17

1 Answers1

4

I think you're saying you have a runtime value and you want to know if it's a valid member of ControlId (you've said you want a "range check," but numeric enums aren't necessarily contiguous).

A const enum has no runtime existence, so you can't write runtime code to validate a number to see if it's a valid member of a const enum.

You can do it if the enum isn't const:

enum ControlId {
    ZoomIn = 600,
    ZoomOut,
    Close, 
    Open,
    // ....
}

function assertIsControlId(value: number): asserts value is ControlId {
    if (!(value in ControlId)) {
        throw new Error(`Invalid ControlId value ${value}`);
    }
}

There I've done it as a type assertion function, but that could be a type guard function instead, or just an inline expression. Basically, if the value is a number, and the value (when converted to string, implicitly by the in operator) is a valid property name for ControlId, it's a valid member of ControlId.

If you really mean you want the min/max, even though the enum values are not guaranteed to be contiguous, you can do that like this:

function getMinMaxOfEnum(e: object) {
    const values = Object.keys(e).map(k => k === "" ? NaN : +k).filter(k => !isNaN(k)).sort((k1, k2) => k1 - k2);
    return [values[0], values[values.length - 1]];
}

(There's probably some better type for e than object. And in modern environments, values[values.length - 1] could be values.at(-1).)

That gets all of the keys from the object you pass in, converts them to number if possible, removes the ones that didn't convert successfully (so now we have an array of enum values), sorts the resulting array, and returns the first and last elements of the array.

Alternatively, use Math.min and Math.max instead of sorting:

function getMinMaxOfEnum(e: object) {
    const values = Object.keys(e).map(k => k === "" ? NaN : +k).filter(k => !isNaN(k));
    return [Math.min(...values), Math.max(...values)];
}

Playground link

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875