Yesterday we deployed Flow v0.21.0! As always, we've listed out the most interesting changes in the Changelog. However, since I'm on a plane and can't sleep, I thought it might be fun to dive into a couple of the changes! Hope this blog post turns out interesting and legible!
JSX Intrinsics
If you're writing JSX, it's probably a mix of your own React Components and some intrinsics. For example, you might write
render() {
return <div><FluffyBunny name="Fifi" /></div>;
}
In this example, FluffyBunny
is a React Component you wrote and div
is a
JSX intrinsic. Lower-cased JSX elements are assumed to be intrinsics by React
and by Flow. Up until Flow v0.21.0, Flow ignored intrinsics and gave them the
type any
. This meant Flow let you set any property on JSX intrinsics. Flow
v0.21.0 will, by default, do the same thing as v0.20.0, However now you can
also configure Flow to properly type your JSX intrinsics!
Example of how to use JSX intrinsics
.flowconfig
[libs]
myLib.js
myLib.js
// JSXHelper is a type alias to make this example more concise.
// There's nothing special or magic here.
// JSXHelper<{name: string}> is a React component
// with the single string property "name", which has a default
type JSXHelper<T> = Class<ReactComponent<T,T,mixed>>;
// $JSXIntrinsics is special and magic.
// This declares the types for `div` and `span`
type $JSXIntrinsics = {
div: JSXHelper<{id: string}>,
span: JSXHelper<{id: string, class: string}>,
};
myCode.js
<div id="asdf" />; // No error
<div id={42} />; // Error: `id` prop is a string, not a number!
What is going on here?
The new bit of magic is this $JSXIntrinsics
type alias. When Flow sees
<foo />
it will look to see if $JSXIntrinsics
exists and if so will grab
the type of $JSXIntrinsics['foo']
. It will use this type to figure out which
properties are available and need to be set.
We haven't hardcoded the intrinsics into Flow since the available intrinsics will depend on your environment. For example, React native would have different intrinsics than React for the web would.
Smarter string refinements
One of the main ways that we make Flow smarter is by teaching it to recognize more ways that JavaScript programmers refine types. Here's an example of a common way to refine nullable values:
class Person {
name: ?string;
...
getName(): string {
// Before the if, this.name could be null, undefined, or a string
if (this.name != null) {
// But now the programmer has refined this.name to definitely be a string
return this.name;
}
// And now we know that this.name is null or undefined.
return 'You know who';
}
}
New string refinements
In v0.21.0, one of the refinements we added is the ability to refine types by comparing them to strings.
This is useful for refining unions of string literals into string literals
function test(x: 'foo' | 'bar'): 'foo' {
if (x === 'foo') {
// Now Flow understands that x has the type 'foo'
return x;
} else {
return 'foo';
}
}
And can also narrow the value of strings:
function test(x: string): 'foo' {
if (x === 'foo') {
// Now Flow knows x has the type 'foo'
return x;
} else {
return 'foo';
}
}
This is one of the many refinements that Flow currently can recognize and follow, and we'll keep adding more! Stay tuned!