Hacker News new | ask | show | jobs
by ggregoire 3361 days ago
For those still using propTypes, I'd recommend to take a look at Flow as replacement.

https://flow.org/en/docs/frameworks/react

7 comments

We use Flow everywhere now and love it. It's not perfect by any means but it's a lot easier to use than Java's static typing and a lot more expressive to boot. I can't overstate how great it is to be able to strongly type your modules and ensure you don't send the wrong input or receive the wrong input from them.

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.

Flow is quite good, but over half of our React codebase is components connected to Redux or Redux Form and unfortunately there are no Flow types for the versions that we use, so type inference is very weak at some points.

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.

> This has the incredible effect of making it a type error if you try to execute an action that doesn't exist.

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

Sure, but with ES2016 and Flow, JavaScript becomes a completely different language. And with V8, it's very fast. I actually really enjoy writing JavaScript now, and I recently realized that I like it more than Ruby.
Yes - and no. Flow can express some really complex types that were previously only possible in much more esoteric (or modern) languages. Types as many people know them from Java, C#, etc., are more mature (aka fewer bugs), but far less powerful.

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.

I agree, but for those considering using flow please beware that it's not a trivial undertaking. I recently converted a small app. There were several mind benders—beyond basic types, annotations can get pretty tricky—and the tooling isn't quite seamless. This is not to discourage you. You may well decide it's worth the effort.
Could you talk about what to watCh for ? We are looking to convert an app of our own as well.
Flow is not a replacement for propTypes, they are complementary. Flow for compile-time errors, propTypes for runtime errors.
For those who want the best of both worlds:

https://codemix.github.io/flow-runtime

It is though. Runtime type validation is just a clutch that's needed when you don't have compile time validation.
Technically true, but how many projects actually are 100% typed, including:

* 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.

You could make some of the same arguments against prop types - what if you miss them, what if they are incorrect (too narrow).

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.

Let's not forgot prop type checks are removed during build (if you are building). So they don't happen in production. Like some kind of type erasure, but not :D.
Not necessarily - you could be parsing a JSON API response or something.
If you don't trust your JSON input to be of proper type, you should validate its type immediately when you receive it, before you start fanning it out to your code. Spreading this work to prop types just makes it harder to keep track of it, and harder to find the source of the problem.

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

Just in case any one else is interested, I wasn't sure what the difference between Flow and TypeScript was. Here's a fairly comprehensive list:

https://github.com/Microsoft/TypeScript/issues/1265

You can also generate PropTypes from your Flow types, which can be useful during development: https://github.com/brigand/babel-plugin-flow-react-proptypes

(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.

Flow’s interesting, unfortunately it requires more invasive changes to your code than just prop types. For example, if I remember correctly, you need a declaration for a class component’s state.
We decided to use Flow in a rewrite of an existing React project at work. I found all of the type boilerplate around React/Redux to be somewhat cumbersome at first, but after a few days I didn't even think about it anymore. It feels a little strange writing React code without it at this point.
Same, we're using Flow in for our React Native app but not yet in our older webapp.

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.

I mean if you really want to be loose with the type of your component's state, you can just declare its type as Object, eg.

    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
That's true, but I think that's a very good thing. It can be very useful to define "StateTypes", as well as PropTypes. I think I've caught more than a few bugs by defining types for my state.
Definitely doesn't look better though...