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

102 Views Asked by At

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.

1

There are 1 best solutions below

0
mochaccino On BEST ANSWER

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