javascriptflowtypeflow-typed

How to get Union type from an array of objects in Flow?


For instance, imagine having the following list of items:

const items = [
   {
       key: "amalgama"
   },
   {
       key: "torin"
   },
   {
       key: "mascarpone"
   }
]

What Flow type ItemsKeyTuple definition should be to get the following check?

type ItemsKeyUnion = ...some stuff with items...

// so I would get the same to the following:
// type ItemsKeyUnion = “amalgama” | “torin” | “mascarpone”;

let a: ItemsKeyTuple;

a = "amalgama" // OK;

a = "tomatoes" // ERROR;

The main idea is to get instant check and help when using inside if-else, switch statements, taking into account a possibility to change keys in some moment in future and be sure that I would be noticed with Flow about places I need to adjust.


Solution

  • For this to work, you would have to infer the tuple's key values as literal string types. Unfortunately, this doesn't seem to be possible in Flow (unlike TypeScript), see facebook/flow#2639. One of the workarounds would be to use an object type, utilizing the $Keys utility:

    const dict = {
        "amalgama": {},
        "torin": {},
        "mascarpone": {},
    };
    
    type ItemsKeyTuple = $Keys<typeof dict>;
    
    const items = Object.keys(dict).map((key) => ({ key }));
    

    Then, you can use Object.keys to convert the dict into the original items array at runtime. Notice that I have subtly used {} as a value for the keys in dict. This is because if you have more properties other than key, you can put them in this empty object:

    const dict = {
        "amalgama": { foo: 42 },
        "torin": { bar: true },
        "mascarpone": { baz: "hello" },
    };
    
    type ItemsKeyTuple = $Keys<typeof dict>;
    
    const items = Object.keys(dict).map((key) => ({ key, ...dict[key] }));
    

    Playground