Given a function that has an argument supposed to be an enum
. The enum
passed in can have different numbers of properties. How to fix the type of that argument ? enum
itself is not a type.
E.g. :
function (myEnum: mysteriousType){
//What is that mysteriousType ?
}
With what you've stated so far (needs to accept all string / numeric / heterogeneous enums), the closest I can do is something like this:
type Enum<E> = Record<keyof E, number | string> & { [k: number]: string };
function acceptEnum<E extends Enum<E>>(
myEnum: E
): void {
// do something with myEnum... what's your use case anyway?
}
enum E { X, Y, Z };
acceptEnum(E); // works
I'm not sure what you're going to do with myEnum
if all you know is that it's "some enum type", but I guess that's up to you to figure out.
How I came up with this: I examined a bunch of concrete enum
types, and they seem to have properties with string keys and string or numeric values (the forward mapping), as well as a numeric index key with string values (the reverse mapping for numeric values).
const works: { X: 0, Y: 1, Z: 2, [k: number]: string } = E; // works
The language designers might have constrained this further, since the reverse mapping will only produce the specific numeric keys and string values seen in the forward mapping, but for some reason it's not implemented like that:
const doesntWork: { X: 0, Y: 1, Z: 2, [k: number]: 'X' | 'Y' | 'Z' } = E; // error
const alsoDoesntWork: { X: 0, Y: 1, Z: 2, 0: 'X', 1: 'Y', 2: 'Z' } = E; // error
So the tightest constraint I can put on an enum type is the above E extends Enum<E>
.
Note that this code does not work for const enum
types which don't really exist at runtime:
const enum F {U, V, W};
acceptEnum(F); // nope, can't refer to `F` by itself
And also note that the above type (E extends Enum<E>
) allows some things it maybe shouldn't:
acceptEnum({ foo: 1 }); // works
In the above, {foo: 1}
is plausibly a numeric enum similar to enum Foo {foo = 1}
but it doesn't have the reverse mapping, and if you rely on that things will blow up at runtime. Note that {foo: 1}
doesn't seem to have an index signature but it still matches an index signature implicitly. It wouldn't fail to match unless you added some explicit bad value:
acceptEnum({foo: 1, 2: 3}); // error, prop '2' not compatible with index signature
But there's nothing to be done here. As I mentioned above, the implementation of enum
typing currently does not constrain the numeric keys as much as it can, so there seems to be no way at compile time to distinguish between an enum with a good reverse mapping and one without one.
Hope that helps. Good luck!