Search code examples
typescriptenumsgrpctypescript-typings

Typescript complains when mapping numeric enum to string enum


I'm trying to map a number-based enum generated from a gRPC proto file into a string-based enum for use in my REST API.

The mapping works but TS complain, giving this error.

What have I done wrong, and how to fix?

types/mapping.ts:5:14 - error TS2739: Type '{ 3: OperationMode.LEARNING; 2: OperationMode.MAINTENANCE; 1: OperationMode.NORMAL; 4: OperationMode.TESTING; 0: undefined; }' is missing the following properties from type '{ 0: OperationMode | undefined; OPERATION_UNSPECIFIED: OperationMode | undefined; OPERATION_NORMAL: OperationMode | undefined; ... 6 more ...; 4: OperationMode | undefined; }': OPERATION_UNSPECIFIED, OPERATION_NORMAL, OPERATION_MAINTENANCE, OPERATION_LEARNING, OPERATION_TESTING

5 export const operationModeG2R: {[key in gRPCOperationMode]: OperationMode|undefined} = {
               ~~~~~~~~~~~~~~~~

Code generated by @grpc/proto:

export const OperationMode = {
  OPERATION_UNSPECIFIED: 0,
  OPERATION_NORMAL: 1,
  OPERATION_MAINTENANCE: 2,
  OPERATION_LEARNING: 3,
  OPERATION_TESTING: 4,
} as const;

Code defined in rest-api.ts

export enum OperationMode {
    NORMAL = 'NORMAL',
    MAINTENANCE = 'MAINTENANCE',
    LEARNING = 'LEARNING',
    TESTING = 'TESTING',
  }
import { OperationMode as gRPCOperationMode } from "../proto/generated/companyname/control/OperationMode";
import { OperationMode } from "./rest-api";

export const operationModeG2R: {[key in gRPCOperationMode]: OperationMode|undefined} = {
    [gRPCOperationMode.OPERATION_LEARNING]:OperationMode.LEARNING,
    [gRPCOperationMode.OPERATION_MAINTENANCE]:OperationMode.MAINTENANCE,
    [gRPCOperationMode.OPERATION_NORMAL]:OperationMode.NORMAL,
    [gRPCOperationMode.OPERATION_TESTING]:OperationMode.TESTING,
    [gRPCOperationMode.OPERATION_UNSPECIFIED]:undefined
}

Solution

  • gRPCOperationMode is a runtime value and can not be used inside of a mapped type because mapped types can only work with types.

    You want to get the type of gRPCOperationMode using typeof and map over its keys with keyof. But the keys of gRPCOperationMode are string types. Since you want to get the number literal type of each key's value, you need to use key remapping to map each key K into its corresponding type gRPCOperationMode[K].

    export const operationModeG2R: { 
      [K in keyof typeof gRPCOperationMode as typeof gRPCOperationMode[K]]: 
        OperationMode | undefined
    } = {
        [gRPCOperationMode.OPERATION_LEARNING]:OperationMode.LEARNING,
        [gRPCOperationMode.OPERATION_MAINTENANCE]:OperationMode.MAINTENANCE,
        [gRPCOperationMode.OPERATION_NORMAL]:OperationMode.NORMAL,
        [gRPCOperationMode.OPERATION_TESTING]:OperationMode.TESTING,
        [gRPCOperationMode.OPERATION_UNSPECIFIED]:undefined
    }
    

    Playground