Shitty code, version 1:
const concat = s => s.reduce((r,s) => r + s)
const mult = (a, b) => a * b
const foo = a => b => c => concat([c, ' = ', mult(a, b)])
const bar = a => Number(a) + 1
const baz = b => b ? 42 : 33
const res = foo(1)('bar')('baz')
console.log(res)
Output: NaN
Oh no, I have a bug (no shit, Sherlok)
Let's include few type intel and check if it can help me, shitty code version 2:
/* @flow */
const concat = (s /*:string[]*/) => s.reduce((r,s) => r + s)
const mult = (a/*:number*/, b/*:number*/) => a * b
const foo = a => b => c => concat([c, ' = ', mult(a, b)])
const bar = a => Number(a) + 1
const baz = b => b ? 42 : 33
const res = foo(1)('bar')('baz')
console.log(res)
Changes:
- Run
flow init
- Add
/* @flow */
- Add type info on a few parameters
- Cool fact #1: as Flow infers types, I only need to type root functions, and even here I can only type their parameters and flow will guess return type
- Cool fact #2: I can use unintrusive comment syntax, allowing me to not add a build step
- Not so cool fact: I must use parenthesis around parameter of arrow functions to add type information, or flow will yell about syntax
Then run flow
:
- I can see that
mult(a, b)
is a number, which is not a valid type forconcat
. OK, I'll cast. - I can also see that
b
is a string which is not a valid type formult
. OK, I'll check my parameters.
Shitty code, final version:
/* @flow */
const concat = (s/*:string[]*/) => s.reduce((r,s) => r + s)
const mult = (a/*:number*/, b/*:number*/) => a * b
const foo = a => b => c => concat([c, ' = ', String(mult(a, b))])
const bar = a => Number(a) + 1
const baz = b => b ? 42 : 33
const res = foo(1)(baz(true))('baz')
console.log(res)
Output: baz = 42
That's cool! Note that using eslint, I would have known since the beginning that I forgot to call baz
(which was supposed to provide my number)
And no build required.