简体   繁体   中英

For a type defined as a map of enum values to strings, how to check with flow that the map object literal has a key for every enum value?

Let's say I have this type

type Status = "draft" | "validated" | "private" | "available" | "archived";

I'm instantiating objects that map this enum type to a string:

const statusNameMap: { [Status]: string } = {
  "draft": "Draft",
  "validated": "Validated",
  "private": "Private",
  "available": "Available",
  "archived": "Archived",
};

This works just fine. Now the question is, how do I check at the type level that the object literal has to contain as keys all of the values from the Status enum?

The main motivation is that the Status type is not in my control really, that's code being generated from introspecting a GraphQL schema, while the object map is for presentation purposes. If an enum value gets added/removed, I would like Flow to complain about my object literal not having the right keys.

Is this possible with Flow at the moment?

No - at least not in the way you'd like. As far as I know this is not supported and has been a great deal of stress for me.

However, you can use $ObjMap to probably get what you want.

const MyEnumValues = Object.freeze({
  KEY_A: 'VALUE_A', 
  KEY_B: 'VALUE_B', 
  KEY_C: 'VALUE_C', 
});
const Test8: $Exact<$ObjMap<typeof MyEnumValues, () => string>> = ({
    'KEY_A': '',
    'KEY_B': '',
    //'KEY_C': '', // Errors here!
});

Resulting in a type that maybe looks like this under the hood:

$Exact<$ObjMap<typeof MyEnumValues, () => string>> = {|
  KEY_A: string,
  KEY_B: string,
  KEY_C: string,
|}

So kindof.If your keys and values are the same then this is probably exactly what you want but its still frustrating.

Here are my test cases I was running to obtain a simple Enum set to Value set here . I also included the code here in case try flow ever goes offline.

/* @flow */

export const MyEnumValues = Object.freeze({
  KEY_A: 'VALUE_A', 
  KEY_B: 'VALUE_B', 
  KEY_C: 'VALUE_C', 
});

export type MyEnum = $Values<typeof MyEnumValues>;
type ExactMyEnumMap = $Exact<{| +[key: MyEnum]: string |}>;
// NOTE: Needs $Exact bc theres another bug here - only using `|` doesn't work.
//       Still doesn't map one-to-one
const Test1: ExactMyEnumMap = {
    [MyEnumValues.KEY_A]: '',
    // NOTE: Should error...
};
// Exact explicit object literal type
const Test2: ExactMyEnumMap = ({
    [MyEnumValues.KEY_A]: '',
    // NOTE: Should error...
})
// Explicit object map type.
const Test3: $ObjMap<{| a: 1, b: 2 |}, () => string> = ({
    a: '',
    b: ''
    // NOTE: WORKS -> Except that you have to "redefine" tye type again...
})
// Explicit exact map object literal type. (Ext type)
const Test4: $Exact<$ObjMap<ExactMyEnumMap, () => string>> = ({
    [MyEnumValues.KEY_A]: '',
    // NOTE: Should error...
})
// Explicit object map object literal type.
const Test5: $ObjMap<{| [MyEnum]: string |}, () => string> = ({
    // NOTE: Should error...
})
// Map function type.
const Test6 = (lookup: MyEnum)/* string - same with explicit */ => {
    switch (lookup) {
        case MyEnumValues.KEY_A:
            return '';
        case MyEnumValues.KEY_B:
            return '';
        // NOTE: Should error...
        //       Typescript reports "not all code paths return a value"
    }
}
// Explicit map type.
const Test7: Map<MyEnum, string> = new Map([
    // NOTE: Probably shouldn't error but was hoping it would.
]);
// Explicit map type (KEYS ONLY).
const Test8: $Exact<$ObjMap<typeof MyEnumValues, () => string>> = ({
    'KEY_A': '',
    'KEY_B': '',
    //'KEY_C': '', // YES!
});

You can use exact type for this case and define type with {|... |} It will looks like

 const statusNameMap: { [Status]: string } = {|
  "draft": "Draft",
  "validated": "Validated",
  "private": "Private",
  "available": "Available",
  "archived": "Archived",
|};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM