Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript enum generic mapping

Tags:

typescript

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.

like image 985
Tomas Avatar asked Jul 02 '18 07:07

Tomas


1 Answers

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;
like image 63
Titian Cernicova-Dragomir Avatar answered Oct 20 '22 21:10

Titian Cernicova-Dragomir