Photo by Jo Szczepanska on Unsplash

How to upgrade to exact-by-default object type syntax

Jordan Brown
Flow
Published in
4 min readJan 29, 2020

--

Almost a year and a half ago, we announced our plans to make object types exact by default. We’ve consistently heard from our users that they almost always mean to use exact object types, so making them the default makes it easier for developers to express their intent. At Facebook, we already have a few repositories internally using this setting and have seen many open source projects adopt the new syntax as well. Eventually, exact-by-default will be the norm in Flow, so we want to help you get your own codebase onto exact-by-default object types.

Getting your codebase to exact-by-default syntax has three phases:

  1. Upgrading dev tools. Ensure all your tools support the new syntax.
  2. Codemodding. Upgrade your codebase to use the new syntax.
  3. Upgrading dependencies. Upgrade any code dependencies to use the new syntax.

These phases can all be done incrementally, meaning you can make progress over time and do not need to make the switch all in one massive commit. In the rest of the post, I’ll go over each phase and offer some tips in places where we encountered issues.

Upgrading Dev Dependencies

Goal: Make sure all your dev tools can parse the new syntax

JavaScript codebases tend to use a ton of dev tools that parse the code. Here are some examples of tools you may use and the versions to which you you need to upgrade. If you try to use the {...} syntax before you upgrade one of these tools, then that tool will crash with a parse error.

  1. Flow >=0.84.0 (recommend 0.112.0+)
  2. Babel/Babel Parser/Babel Generator >= 7.1.5 (recommend 7.4.5+)
  3. Prettier >= 1.15 (recommend most recent)
  4. babel-eslint >= v11-beta

After you upgrade all of those tools, try putting explicit inexact object types {...} manually in a few spots in your codebase and let your automated tests run. If you get any parser related failures, then the stack traces might be able to guide you towards other packages you need to upgrade.

Codemodding

Goal: Be able to turn on implicit-inexact-object=error in your flowconfig without getting any errors

After your repo is set up to parse the new implicit inexact object types, you can start codemodding your codebase. Before turning on exact-by-default, you’ll want to make sure that every object type in your codebase is either explicitly exact {||} or explicitly inexact {...}. That way, you can turn on exact-by-default without getting any new errors.

There are two ways to codemod your codebase:

  1. Use flow-upgrade. This will try to upgrade your entire codebase at once. You can invoke flow-codemod convertImplicitInexactObjectTypes to run the codemod.
  2. Use jscodeshift directly. In our experience, it’s easier to use this for larger codebases where you don’t want to run the codemod on the entire codebase in one go. You can use jscodeshift -t /path/to/transform.js TARGET_DIR to run the codemod. Get the transform in this gist.

Both of these methods may miss some occurrences of implicit inexact objects. You may need to do a manual pass by-hand after running the codemods.

Tips

  • If you have files shared between different repositories, add // flowlint ambiguous-object-type:error to the top of those files. This will make sure that all object types are explicitly exact {||} or explicitly inexact {...} regardless of the exact-by-default settings. This guarantees that your shared files will always mean the same thing across your repositories even if they have different exact-by-default settings.

Upgrading Library Definitions

All of flow-typed was upgraded to use exact-by-default compatible syntax. Any flow-typed definition that is for flow 0.104+ will use only explicit exact object types {||} or explicit inexact object types {...}.

Finishing up the codemods

Once you can turn on implicit-inexact-object=error in the [lints] section in your flowconfig without getting any errors you should move on to the next section. Make sure you push that change to the flowconfig so that other collaborators do not continue to introduce new implicit inexact object types.

Upgrading Dependencies

Goal: Be able to turn on exact-by-default without any errors

Some of your dependencies may ship with their own .js.flow files and do not have flow-typed definitions. Those projects may not be using exact-by-default, so in those cases you have two options:

  1. Add // flowlint ambiguous-object-type:error to all the exported object types in those files and upstream the changes.
  2. Make flow-typed definitions and use // flowlint ambiguous-object-type:error there.

Both options will also make it easier for other people in the community to upgrade their own codebases.

To find the dependencies that have those issues, turn on exact_by_default=true in the options section of your flowconfig. If you have no errors, then you’re already set to go. Otherwise, inspect the messages to see which packages in the node_modules the errors reference.

Once you’ve fixed all those errors, your codebase is officially exact-by-default!

Now you can get rid of the explicitly exact object types by using flow-codemod removeExplicitlyExactObjectTypeSyntax.

Conclusion

Good luck with the codemods! When you finish, you’ll have an easier time expressing your intent to Flow without falling into accidental gotchas. If you have any questions, please ask them on this post so that other people having similar issues can see how you solve your problems.

--

--