Hiding Implementation Details With Flow’s New Opaque Type Aliases Feature

Jordan Brown
Jul 27, 2017 · 4 min read

Do you ever wish that you could hide your implementation details away from your users?

Well, now all of your dreams have finally come true! Flow 0.51.0 added support for opaque type aliases, with babel support coming in the next week or so. Opaque type aliases are type aliases that hide their underlying type. You can only see an opaque type’s underlying type in the file which declares the opaque type. They’re already documented here, so we’ll spend the rest of this blog post showing just how powerful opaque type aliases can be.

Maintaining Invariants with Opaque Types

Opaque type aliases are really useful for maintaining invariants in your code. Whenever you find yourself wanting to express “things of type T where X is true,” you might want to consider using an opaque type alias.

As a simple example, lets consider a type for non-negative numbers:

NonNeg.js:// @flowopaque type NonNeg = number;

Now we can make some functions to interact with NonNeg numbers:

NonNeg.js:// @flowopaque type NonNeg = number;export function add(x: NonNeg, y: NonNeg): NonNeg {
return x + y;
}
// Returns 0 at minimum
export function subtract(x: NonNeg, y: NonNeg): NonNeg {
return Math.max(0, x - y);
}
export function zero(): NonNeg {
return 0;
}
export function increment(x: NonNeg): NonNeg {
return x + 1;
}

Making an opaque type alias is like making a contract with your user: if they agree to only use your library to interact with your type, you will maintain any invariant your type guarantees.

Your users can now go forth and use your type, always knowing it won’t be a negative number:

imports.js:// @flowimport {add, subtract, zero} from './NonNeg';const w = zero();
const x = increment(w);
const y = add(w, x);
const z = subtract(y, w);

Now suppose one of our users tries to break our invariant:

bad-usage.js:// @flowimport {zero} from './NonNeg';let a = zero();
let b: NonNeg = a - 1;

Since we used an opaque type alias, which doesn’t expose its type outside of its defining file, they’ll get an error:

Error: a = a - 1;
^ Error: a is NonNeg, which is incompatible with number.

Awesome! We were able to use Flow to protect our NonNeg invariant! But this might be a bit too restrictive. After all, a non-negative number is still a number, so we should be able to use it as such.

Subtyping Constraints

We can add a subtyping constraint to our opaque type alias to express that it can still be used as a number outside of the file.

NonNeg.js:// @flowopaque type NonNeg: number = number;/* ... */

Adding : number to our opaque type alias tells Flow that every NonNeg is a number. This does not imply, however, that every number is a NonNeg.

Now let’s take another look at the client code. There is still an error!

let b: NonNeg = a - 1;
^ Error: number. This type is incompatible with
let b: NonNeg = a - 1;
^ opaque type `NonNeg`.

Notice that now Flow interprets a — 1 as a number, but won’t allow it to be assigned to a variable that is a NonNeg. If we wanted to use the value of a as a number, we’ll have to use it in a place that expects a number.

let b: number = a - 1; // Ok!

To continue the metaphor, this is the user breaking your contract. As soon as they stop using your library functions, you no longer guarantee that the number is non-negative.

Some more use cases

  • Are you using strings to represent unique IDs in your application? Not all strings are IDs — you can use an opaque type alias to make sure that no random strings get passed around pretending to be IDs!

Conclusion

Opaque types in Flow are a contract you make with the users of your type. The contract can be eternally binding, where you user has no choice but to use your functions. Alternatively, you can provide a subtyping escape hatch to your user, allowing the user to break the contract whenever they please.

Are you building cool things with Flow or opaque type aliases? We want to hear from you! Send a tweet to flowtype on Twitter so the whole community may celebrate your efforts to make writing JavaScript delightful.

Flow

The official publication for the Flow static type checker…

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store