Help us understand the problem. What is going on with this article?

Flowtype+reduxにおけるreducerの正しい型付け

More than 1 year has passed since last update.

よく以下のような型付けを見ます。

// @flow
import { Actions, type Action } from './actions'

export type State = {
  count: number,
  other: string,
}

const initialState: State = { count: 0, other: 'test' }

export default function(state: State = initialState, action: Action): State {
  switch (action.type) {
    case Actions.INCREMENT:
      return { ...state, counter: state.count + 1 }
    default:
      return state
  }
}

一見正しいように見えます。
flowの型エラーはでません。
しかし、よく見ると、countcounterとタイポしています。
これでは型安全などと言えませんね。

$Shape<T>を使った改善

この型のオブジェクトは、Tに記述されていないプロパティを含むことはできません。

type T = {
  foo: string,
  bar: number
}
const a: $Shape<T> = {
  foo: '121', // no errors about not having bar 
  baz: true   // error, baz is not in T type
}

これをreducerで使うと以下のようになります。
reducerの戻り値の型を$Shape<State>に変更します。

スクリーンショット 2017-09-16 17.18.05.png

グレート!
定義されてないcounterで型エラーが起きました。
countに直しましょう。

スクリーンショット 2017-09-16 17.19.55.png

エラーが消えました。
しかし、残念ながら、これではダメです。
定義したStateの型は以下でした。

type State = {
  count: number,
  other: string,
}

プロパティotherが消えているのに検知出来ていません。

T & $Shape<T> を使う

これを解決するには以下の型を定義します。

type Exact<T> = T & $Shape<T>

これは、Tのプロパティ全てかつそれ以外のプロパティを許容しない型です。

するとプロパティが足りない場合正しくflowが検知します。

スクリーンショット 2017-09-16 17.26.35.png

...stateを追加しましょう。
これで、reducerが型で守られるようになりました。

スクリーンショット 2017-09-16 17.54.36.png

全体では、以下のようになります。もちろんExactはreducerごとに定義するのではなくimport typeした方がよいでしょう。

スクリーンショット 2017-09-16 18.08.03.png

なぜ$Exact<T>ではダメなのか?

$Exact<T>だと全てのオブジェクトが正しくないとダメなので、{...state}を使うとその時点でエラーが起きてしまいます。
なので{ ...state, count: state.count + 1 }で型エラーが発生してしまいます。

スクリーンショット 2017-09-16 18.17.35.png

追記

{| |}$Exact<T>と同等です。

おわりに

上記のスクリーンショットのエディタはAtom-IDEを使っています。

Exact<T>ですが、それぞれのreducerに毎回import typeするか悩みますね。
どこでも使えるようにグローバルに定義してしまってもいいかもしれないとも思っています。
何かあればコメント欄またはtwitterにて議論しましょう。

関連記事

Flowtypeのactionの型付け。String Literal型とString型について
http://qiita.com/akameco/items/e7021e22da4c9e14463a

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした