typescriptenumsoptional-parametersfunction-definitiongeneric-type-parameters

Typescript Return the enum values in parameter using a generic enum type method


I need to return a list with all values of an Enum (specified as parameter or generic). If the user has a specific role I need only some of the Enum values given as parameter.

I considered to write the two parameters as a tuple because they are either given both or none.
My first try would look like this:

getItemsBasedOnRole<T extends enum>(...[role, items]?: [UserRole, T[]]) : string[]
{
    const allValues = Object.values(T);
    if (role && items) // tuple parameter exists
        if (this.securityService.getUser.role === role)
            return items;

    return allValues;
}

const result = getItemsBasedOnRole<MyEnum>([ClientRole], [MyEnum.Value1, MyEnum.Value3]);
// returns [MyEnum.Value1, MyEnum.Value3]

const result = getItemsBasedOnRole<MyEnum>();
// returns [MyEnum.Value1, MyEnum.Value2, MyEnum.Value3]


export enum MyEnum{
    Value1 = 'Value1',
    Value2 = 'Value2',
    Value3 = 'Value3',
}

The problems are in Typescript:

My last attempt, which shows the errors in the above list:

getItemsBasedOnRole<T extends { [s: number]: string }>(enumValue: T, ...[role, items]: [UserRole, T[]]): string[]

The above problems are related to types and function definition and I just want to return a dynamic list. I am open to any ideas: multiple functions, using parameter instead of generic type, etc.


Solution

  • I would suggest using constant objects instead of enums, since they are more predictable and easier to manipulate. We will need to apply const assertion to prevent the compiler from widening the type of the object and to make sure that the object is type-safe, we are going to use satisfies operator:

    export const MyEnum = {
      Value1: 'Value1',
      Value2: 'Value2',
      Value3: 'Value3',
    } as const satisfies Record<string, string>
    

    Since, our enum is Record<string, string> we will change the constraint of the generic argument to the relevant one:

    declare function getItemsBasedOnRole<T extends Record<string, string>>(obj: T, ...args:[something]) {}
    

    To get the values of the whole object, we are going to use indexed access: T[keyof T] will give the union of all values:

    declare function getItemsBasedOnRole<T extends Record<string, string>>(obj: T, ...args: [role?: UserRole, values?: T[keyof T][]]): string[]
    

    Usage:

    getItemsBasedOnRole(MyEnum, 'role', ['Value1', 'Value2'])
    

    playground