typescript

typescript function to map through enum values searching for specific value


I've made an API call that returns a string, for example "clear". I've made an enum SkyGradients to match this string:

export enum SkyGradients {
  Overcast = "linear-gradient(135deg, #808080, #404040)",
  Clear = "linear-gradient(135deg, #C0C0C0, #808080)",
}

as well as SkyColors:

export enum SkyColors {
  Overcast = "rgb(128, 128, 128)",
  Clear = "rgb(192, 192, 192)",
}

Now what I want to do is get the corresponding color and gradient using the string: "Clear"

I've tried this:

export function getGradients(text: string): string | undefined {
    for (const gradient in Gradients) {
        if (Gradients[gradient] === text) {
            return gradient;
        }
    }
}

But I'm getting this error:

'Gradients' only refers to a type, but is being used a value here.

as well as:

Element implicitly has an 'any' type because of type 'string' can't be used to index type 'typeof Gradients'. No index signature with a parameter of type 'string' was found on type 'typeof Gradients'

I understand the errors but unsure how to fix


Solution

  • Unless there's some reason you really want an enum pattern, seems like a better solution would be

    export type SkyFills = {
      Overcast: string,
      Clear: string,
      [key: string]: string | undefined // This lets you index with any string
    }
    
    export const skyGradients: SkyFills = {
      Overcast: "linear-gradient(135deg, #808080, #404040)",
      Clear: "linear-gradient(135deg, #C0C0C0, #808080)",
    };
    
    export const skyColors: SkyFills = {
      Overcast: "rgb(128, 128, 128)",
      Clear: "rgb(192, 192, 192)",
    }
    

    Then to access the value it's just

    skyGradients.Overcast // "linear-gradient(135deg, #808080, #404040)"
    skyGradients["Overcast"] // "linear-gradient(135deg, #808080, #404040)"
    skyGradients["someString"] // undefined
    

    TS Playground Example

    See other solutions below.


    Since you're ok with undefined being returned when the key does not exist, you can safely cast the enum to any.

    export function getGradients(text: string): string | undefined {
        return (SkyGradients as any)[text];
    }
    

    TS Playground Example

    Although you open yourself to runtime errors if you ever add values to the enum that aren't strings.


    You can get more robust type safety if you declare objects instead of enums (enums are just compiled down to objects anyway). You can typecast the object to still infer the object's type, but also allow indexing with arbitrary strings. You can even create a type to act like an enum type.

    This eliminates the need for a getter function while maintaining strict type safety and the same functionality as enums. Albeit it's not pretty to look at.

    const _skyGradients = {
      Overcast: "linear-gradient(135deg, #808080, #404040)",
      Clear: "linear-gradient(135deg, #C0C0C0, #808080)",
    } as const;
    
    export type SkyGradients = typeof _skyGradients[keyof typeof _skyGradients];
    
    export const skyGradients = _skyGradients as typeof _skyGradients & {[key: string]: SkyGradients | undefined}
    

    Example usage:

    const test1 = skyGradients.Overcast // type is "linear-gradient(135deg, #808080, #404040)"
    const test2 = skyGradients.Clear // type is "linear-gradient(135deg, #C0C0C0, #808080)"
    const test3 = skyGradients["someString"] // type is SkyGradients | undefined
    
    function someFn(g: SkyGradients) {}
    
    someFn(skyGradients.Overcast) // Ok
    someFn("someString") // Not Ok
    

    TS Playground Example