flow を触ってみて初見殺しだと思ったので特にハマりそうなところをまとめてみた。
関数引数の数を厳密にチェックしたい
基本的に公式でも説明されている下限チェックと上限チェックの両方を同時に使う。
- http://flowtype.org/docs/functions.html#too-few-arguments
- http://flowtype.org/docs/functions.html#too-many-arguments
これで引数の数が固定の場合はチェック可能になる。 1 つしか取らないことを明示したい場合は次のようになる。
function minus (n: number, ...rest:Array<void>): number {
return -n
}
// these will raise errors
minus()
minus(1, 4)
数は変動するんだけどチェックはしたい、という場合は ?
を使う定義を併用する。
例として、 1 個でも 2 個でもいいけど 0 個や 3 個以上は怒ってほしい場合は次のように書く。もちろん flow は型チェックしかしてくれないので、引数の数による分岐は実際の関数定義内に書くことになる。
function minus (a: number, b?: number, ...rest:Array<void>): number {
if (arguments.length === 1) {
return -a
}
return a - b
}
// these will raise errors
minus()
minus(1, 4, 0)
destructuring を使った関数引数の型チェックをしたい
object の型指定を使う。 destructuring で検索してもそのものずばりの例は出てこないので注意。
例。
function foo ({a, b}: {a: number, b: string}): string {
return a.toString() + b
}
// these will raise errors
foo({
a: 'life',
b: 42,
})
foo(42, 'life')
generics を使って型の定義をいちいち書かなくて済むようにしたい
関数の引数の型と返り値の型があっているかどうかをチェックしたい場合などは any ではまかなえない。が、各型についていちいち定義していくとキリがないし、ライブラリーの場合利用者が定義したクラスが使えないなどの弊害がある。
こういう場合は generics を使う。
generics function は次のように。この場合どういう使い方をしてもエラーは飛ばない。はず。
function echo<T> (a: T): T {
return a
}
generics class は次のように書ける。
class List<T> {
items: Array<T>;
constructor (x: Array<T>) {
this.items = x.slice()
}
map<DstType>(f: (x: T) => DstType): List<DstType> {
return new List(this.items.map(f))
}
}
// these are ok
let numbers: List<number> = new List([1, 2, 3])
let strings: List<string> = numbers.map((x: number) => {
return x.toString()
})
// these will raise error
numbers.map((x: string) => {
return parseInt(x, 10)
})
generics とはいうものの特殊化をしたいときは実関数の中で場合分けするしかない。
関数の overload を許可したい
例によって flow は型チェックしか提供しないのでその他言語でいうところの overload とは厳密には違う。関数の実引数に異なる複数の型の値がきてもエラーにしない、というだけ。
やはり次のように関数の定義をするところで実引数の型を見て処理を切り分けないといけない。
declare function add (a: number, b: number): number
declare function add (a: string, b: string): string
declare function add (a: Date, b: number): Date
function add (a, b) {
if (a instanceof Date) {
return new Date(a.getTime() + b)
}
return a + b
}
// these will raise errors
add('life', 42)
let a:string = add(1,3)
let a: Date = add(60 * 60 * 1000, new Date())
add(new Date(), 'foo')