-
Lots of type inference
-
Null/undefined checking
Similar syntax
As an example, I ported my [EventEmitter](https://github.com/voltrevo/voltrevo-event-emitter) module to [Typescript](https://github.com/voltrevo/voltrevo-event-emitter-ts) and [Flow](https://github.com/voltrevo/voltrevo-event-emitter-flow) and they are almost identical.-
Does a lot for you, especially when paired with VS Code, so your project requires less set-up
-
Type annotations available for many more third party libraries than Flow
Type information on hover is complete
```js class Actor { act(description: string) { return { undo() { console.log(`undo ${description}`); }, }; } }const actor = new Actor();
// Typescript says that action
is a { undo: () => void }
, flow just says {}
const action = actor.act('foo');
// Strangely though, flow does know that undo
is a () => void
const undo = action.undo;
In general, VS Code is also able to provide a much better intellisense experience than any Flow plugin available for Atom/Sublime/VS Code. Flow seems similarly capable under the hood though, so it should get better.
</details>
<details>
<summary>Type inference works with generics</summary>
```js
function identity<T>(value: T) {
return value;
}
// Typescript knows that foo is a string. Flow does not. Flow also emits an error if you try to
// add a string type annotation to foo.
const foo = identity('foo');
Better support for 100% typed code via `--noImplicitAny`
```js // While Flow sometimes asks for type information, e.g. for parameters of exported functions, // there are many cases where the `any` type can sneak into your code and prevent type checking. // A common one is missing an index signature of an object:const map = { foo: 'bar' };
function get(key: string) {
// Typescript: Element implicitly has an 'any' type because type '{ foo: string; }' has no index
// signature.
// Flow: Exports get(key: string): any
without warning.
return map[key];
}
However, much of this gap can be closed by using [flow-coverage-report](https://www.npmjs.com/package/flow-coverage-report):
$ flow-coverage-report -i 'src/.js' ┌───────────────────────┬─────────┬───────┬─────────┬───────────┐ │ filename │ percent │ total │ covered │ uncovered │ │ src/Collection.js │ 100 % │ 38 │ 38 │ 0 │ │ src/MapWithDefault.js │ 100 % │ 28 │ 28 │ 0 │ │ src/index.js │ 100 % │ 59 │ 59 │ 0 │ │ src/mapTests.js │ 87 % │ 8 │ 7 │ 1 │ │ src/useMap.js │ 75 % │ 4 │ 3 │ 1 │ └───────────────────────┴─────────┴───────┴─────────┴───────────┘ ┌─────────────────────────┬──────────────────────────────────────────┐ │ included glob patterns: │ src/.js │ │ excluded glob patterns: │ node_modules/** │ │ threshold: │ 80 │ │ generated at: │ Mon Nov 07 2016 16:02:09 GMT+1100 (AEDT) │ │ flow version: │ 0.34.0 │ │ flow check passed: │ yes (0 errors) │ └─────────────────────────┴──────────────────────────────────────────┘ ┌─────────────────────────────┬─────────┬───────┬─────────┬───────────┐ │ project │ percent │ total │ covered │ uncovered │ │ voltrevo-event-emitter-flow │ 98 % │ 137 │ 135 │ 2 │ └─────────────────────────────┴─────────┴───────┴─────────┴───────────┘
Some may actually prefer this to ease the transition into adding typing to a project. On the other hand, others may prefer temporarily turning on Typescript's `--noImplicitAny` to view errors in the editor which would give them feedback in real-time as they add type information.
</details>
<details>
<summary>Correctly identifies `.pop` on a `T[]` as a `T | undefined`</summary>
```js
const nums = [17];
nums.pop();
const num = nums.pop();
// Typescript correctly emits an error here because it has inferred `num` as a `number | undefined`.
// Flow doesn't catch it because it infers `num` as `number`.
console.log(num.toString());
Supports interfaces
```js interface X {} interface Y {}// Typescript: OK. Flow: Error: implements not supported. class Widget implements X, Y {}
Flow can parse the `interface X { ... }` syntax, but without `implements` it seems equivalent to just defining a type (and you'll need [babel-plugin-transform-flow-strip-types](https://www.npmjs.com/package/babel-plugin-transform-flow-strip-types)).
</details>
## Advantages of Flow
- Provides type checking only, so it is more flexible about how you set-up the rest of your project
<details>
<summary>Catches the error when you assign a `Derived[]` to a `Base[]`</summary>
```js
class Animal {}
class Cat extends Animal {
furColor: string;
}
const cats: Cat[] = [];
const animals: Animal[] = cats;
// Flow correctly emits an error here. Typescript does not. It's an error because it makes cats
// no longer conform to type Cat[].
animals.push(new Animal());
// As an example, badFurColor below is inferred as a string, and furColor is
// provided via autocomplete, even though cats[0] is not a cat.
const badFurColor = cats[0].furColor;
Infers types based on usage (not just known input -> operation -> inferred output)
```js function double(x) { return x * 2; // Flow infers x as string because of usage below, and emits an error here }const result = double("foo");
**Note**: With `noImplicitAny`, Typescript emits an error for `x` being implicitly the `any` type.
</details>
<details>
<summary>Doesn't confuse `{}` with `string` and `number`</summary>
```js
// Flow (arguably) correctly emits an error saying string is incompatible with object.
// Typescript doesn't mind.
const foo: {} = 'foo';
// There is a Typescript bug related to this that causes it to emit an error for this code,
// Flow handles it fine though:
const items = ['foo', {}, null];
for (const el of items) {
if (typeof el === 'string') {
console.log(el.toUpperCase());
}
}
More information: microsoft/TypeScript#12077
Mistypes elements of `T[]` accessed via subscript as `T`. They should be `T | undefined` due to the out of bounds case
```js const nums = [1, 2, 3]; const foo: number = nums[100];console.log(foo.toString()); // boom not prevented
</details>
<details>
<summary>Can't declare types in class scope</summary>
```js
class Foo<T> {
type Wrapper = { value: T }; // Both error: 'type' not expected
}
Doesn't catch uninitialized member variables
```js class DefaultGetter { generateDefault: () => string;get() {
// Both typescript and flow don't catch that generateDefault
is undefined
return this.generateDefault();
}
}
const defaultGetter = new DefaultGetter(); const foo = defaultGetter.get();
</details>
<details>
<summary>Allows index signatures with non-nullable value types</summary>
```js
const map: { [key: string]: string } = {
foo: 'bar',
};
const key = 'idontexist';
// Both Typescript and Flow fail to catch this
const oops = map[key].toUpperCase();
This is similar to the array subscript issue. In both cases it shouldn't be possible to have the subscript operator return a non-nullable type, because arrays and objects are unable to define a value for all possible subscripts.
- In Typescript, the type of
undefined
isundefined
, in Flow the type ofundefined
isvoid
@Kriegslustig - the playground doesn't currently enable
strictNullChecks
mode.