In fact, Flow allows us to confidently implement APIs that would just be cumbersome otherwise; we can use complex string keys for UI actions, rendered templates, etc., and make Flow actually enforce them via its $Keys<T> helper. This has the incredible effect of making it a type error if you try to execute an action that doesn't exist.
Cumbersome APIs become easy with Flow, and that's just scratching the surface of its utility on any sufficiently-complex JS project. It's nothing but wins for your development team.
TS is great too. Honestly, either way is a giant leap forward.
I don't want to start a "JavaScript is bad" flamewar, but it's sad that there is an entire class of problems that we as software developers are solving, where simple type checking seems amazing
For example:
The utility types in https://flow.org/en/docs/types/utilities/ can be used to great effect to express things like "A type of the keys of T, but values passed through this function" or "A type that is the difference of these two maps".
In practice, this helps me write things like React Components that take some of their props from the root store via context, and the rest of their props directly, and for Flow to actually know which is which just by reading the code (no explicit typedefs!), override root store props as needed (so long as I don't change their types), and throw a type error if I miss or misdefine one. Awesome.
Besides, Flow has a very ambitious target of delivering types for APIs which were not written with types in mind at all. I thought I'd be able to recreate some of the Elm's type safety with Flow, but that's just not possible and I doubt it'll ever be. But then again, these two projects have very different objectives.
* absolutely no any types or other imprecise types, no files skipped, no lines skipped, etc * every single piece of external data (API requests, reading from localstorage/IndexedDB, etc) is explicitly validated to make sure it exactly conforms to expectations * all dependencies are also typed to the same standard (not just some type definitions slapped on top of an npm module, those could easily be wrong and often are)
Probably some are. But none of mine are, sadly. And I don't think I'm in the minority.
Regarding third party libraries, with prop types you are effectively writing your own bindings to the parts of those libraries that happen to go through your props, except you fail far away from where the error is produced.
Prop types are also a very ad-hoc solution to a very general problem. Why are props for UI components especially deserving of type checking? What about the rest of your application code? Event your components' state is not worthy of type checking?
With JS or typed-JS languages like Typescript or Flow you will never get full type safety (Flow is better at safety than TS if you're interested). For me the solution is to either go 80% of the way for 20% of the effort with those languages, or go full Scala.js or something that is actually type safe. And the latter has many goodies like https://github.com/lihaoyi/autowire to make API calls typed.
Besides, some of that broken JSON data might never even be passed through proptypes (e.g. if that data lives only in a component's state).
Also, there are compile-time solutions to this in some compile-to-JS languages, e.g. like https://github.com/lihaoyi/autowire for Scala.js
(Unfortunately it's not working with React Native, but I hope that's fixed soon: https://github.com/brigand/babel-plugin-flow-react-proptypes...)
EDIT: Thanks to namuol, I just discovered flow-runtime: https://codemix.github.io/flow-runtime
This looks amazing!
EDIT 2: flow-runtime also doesn't work with React Native: https://github.com/codemix/flow-runtime/issues/17
That's a shame. I might see if I can fix it, otherwise I'll just stick with compile-time checks for now.
I've found more bugs that way that more than offset the extra time spent writing types and being a little bit more verbose to keep flow happy.
The minor annoyances are around the edge cases. Like eslint/flow throws propType errors when using props that were dereferenced from `this.props.someObject`. But you can just write the longer version `this.props.someObject.whatever` and everything's fine.
class MyComponent extends React.Component {
state: Object;
constructor(props) {
super(props);
this.state = {
foo: 1, // no type error
};
};
}
Providing a more explicit type for the component's state is a great way to catch bugs, however. It also forces you to more clearly think through which combinations of state properties are valid. Jared Forsyth gave a good talk about this at React Conf: https://www.youtube.com/watch?v=V1po0BT7kac