Forcing allowed object properties/keys with Flow type defined by enumeration object

Ever wondered how to make such thing working right? Me too. So here’s how with none of the $ObjMap<T, F> utility obscurity.

Martin Adamko
2 min readDec 1, 2020

It looks like is should work but it doesn’t. There are few mistakes in our basice assumptions.

tl;dr: Use Object.freeze() and $Exact<T> Flow type utility.

1. Type is not Exact

Defning object type in Flow is by default inexact. Making your object type $Exact<T> would force object to has only the properties/keys defined by the type. You can use short notation {|᠁|} or more explicit, which is always better,$Exact<{᠁}>:

Try it out online →

So where’s the catch?

The culprit lies in the const. It gives us false sense of immutability. However the docs state that:

The value of a constant can’t be changed through reassignment, and it can’t be redeclared. const @ MDN web docs

Assigning an object to a const guarantees that the refference itself won't change. The object itself can be changed by altering the its properties.

The objects in Javascript are not immutable by default. Flow therefore cannot be sure that there won’t be any other keys added to the keys object later in the future and does not raise an error as we would expect.

That’s how React’s useRef() hook passes mutable values using the current property between renders while still maintaining the reference to the ref object without causing unnecessary re-renders.

How can we guarantee and let Flow know that the object won’t change in the future?

2. Object.freeze()

The Object.freeze() method freezes an object. A frozen object can no longer be changed; freezing an object prevents new properties from being added to it, existing properties from being removed, prevents changing the enumerability, configurability, or writability of existing properties, and prevents the values of existing properties from being changed. In addition, freezing an object also prevents its prototype from being changed. freeze() returns the same object that was passed in.Object.freeze() @ MDN web docs

Final solution:

Try it out online →

That’s all folks and let me know how you liked the article!

Martin

--

--

Martin Adamko

One that loves design, illustration, photography, digs in code, adores his dog and enjoys life & good coffee. http://be.net/martin_adamko