10

Helo Stack Community, I have following enum mapping class:

export class RestEnumMpapper<T> {

  constructor() {}

  getEnumAsString<T>(o: T, key: string | number): string {
    if (typeof key === 'string') {
      return (o as T)[(o  as T)[key]];
    } else if (typeof key === 'number') {
      return (o as T)[key];
    } else {
      throw new Error(`Unable to parse enum from ${typeof(key)}`);
    }
  }
  /* ... Rest of enum converting class code ... */
}

And typical use case is following:

    export class PositionEvent {
    private static statusEnumMapper: RestEnumMpapper<EventState> = new RestEnumMpapper<EventState>();
    /* ... */
    this.status = PositionEvent.statusEnumMapper.getEnumAsString(EventState, iPos.status) as EventState;
}

It works pretty well, but I'm currently linting my code and linter complains about shadowing generic type T in RestEnumMpapper class here:

export class RestEnumMpapper<T> {

and here:

getEnumAsString<T>(o: T, key: string | number): string {

Which is rational and make sense to leave generic type as class type. However, when I'm dropping T on function declaration on each call I'm getting following TypeScritp error:

[ts] Argument of type 'typeof EventState' is not assignable to parameter of type 'EventState'.

It can be forced to work by passing value of enum (or any other plain number) but function obviously fails when trying to resolve statement like 2[3].
I'd be thankful for any suggestion how to fix this? Before dropping generic type in function TypeScritp was somehow able to resolve T as object with properties and just a name of enum was sufficient to work.

1 Answer 1

8

This happens because you are not passing in a value of the enum to the function, which would have the type EventState but rather the object containing the enums which has type typof EventState. So removing the type parameter from the method, but passing in typeof EventState to the class should work fine:

let statusEnumMapper = new RestEnumMpapper<typeof EventState>();

Also might I suggest if the class is tied to a single enum as seems to be the case, you might as well pass that to the constructor, and add an index signature to T to allow indexing without casting:

export class RestEnumMpapper<T extends { [name: string]: any }> {

    constructor(public enumObject: T) { }

    getEnumAsString(key: string | number): string {
        if (typeof key === 'string') {
            return this.enumObject[this.enumObject[key]];
        } else if (typeof key === 'number') {
            return this.enumObject[key];
        } else {
            throw new Error(`Unable to parse enum from ${typeof (key)}`);
        }
    }
    /* ... Rest of enum converting class code ... */
}

enum EventState {
    Test = "Test",
    One = "One",
}

let statusEnumMapper = new RestEnumMpapper(EventState);
let statusStr = "One";
let status = statusEnumMapper.getEnumAsString(statusStr) as EventState;
Sign up to request clarification or add additional context in comments.

4 Comments

Hi Titan, first of all thanks as always ;) However this seems a bit confusing to me. You helped me recently to resolve issue with generic object passed to function and being resolved as constructor function (f(obj: () =>T)), so i thought that enums may be handled similarly.
@Tomas yeah, they behave a bit differently ... For classes the thing that represents them at runtime is a function that creates an instance. For enums it's an object that contains all the values. I don't remember the specific answer I gave to your other question, but not everything that works for classes will work the same way for enums :)
@Titan, I think your solution might be improved a bit by using indexing types like <T, K extends keyof T>
@Tomas sure, that could work as well, we could take keyof T as a parameter to getEnumAsString

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.