typescript-fsaなど、TypeScriptとReduxを利用する上でサードパーティライブラリが勧められる事があったが、現状のTypeScript 2.9、3.0-rcで普通に書いてみたところ、素reduxでもVSCodeでわりとサクサク補完されるようだ。
import {
createStore,
Reducer,
Action,
combineReducers,
ActionCreatorsMapObject,
ReducersMapObject,
ActionCreator
} from "redux"
//// State全体の定義
export type AppState = {
counter: number
}
//// ActionとActionCreatorの定義
// type CounterAction = Action<"INCREMENT" | "DECREMENT"> でも可
type CounterActionType = "INCREMENT" | "DECREMENT"
type CounterAction = Action<CounterActionType>
// これだけだるいが利用側のために二重定義してる。後述
export interface CounterActionCreators
extends ActionCreatorsMapObject<CounterAction> {
increment: ActionCreator<CounterAction>
decrement: ActionCreator<CounterAction>
}
export const counterActions: CounterActionCreators = {
increment: () => {
return { type: "INCREMENT" }
},
decrement: () => {
return { type: "DECREMENT" }
}
}
//// Reducer。genericsらへんはちょっと怠けてる
const counterReducer: Reducer = (state = 0, action: CounterAction) => {
switch (action.type) {
case "INCREMENT": // このへんVSCode補完効いて最高の気分
return state + 1
case "DECREMENT":
return state - 1
}
return state
}
export const generateStore = () => {
const reducerMap: ReducersMapObject<AppState> = {
counter: counterReducer
}
return createStore(combineReducers(reducerMap))
}
で、利用側
// Counter sample
import React, { Component } from "react"
import { connect } from "react-redux"
import { bindActionCreators, Dispatch } from "redux"
import {
counterActions,
AppState,
CounterActionCreators
} from "../store"
// State -> Propsに変換する例。
// 変換不要なら type StateProps = AppState でいいだろう
type StateProps = {
cnt: number
}
// 子のPropsはStatePropsとCounterActionCreatorsを持つ
type ChildProps = StateProps & CounterActionCreators
class CounterInner extends Component<ChildProps> {
render() {
return (
<div>
<div>{this.props.cnt}</div>
<button onClick={this.props.increment}>+</button>
<button onClick={this.props.decrement}>-</button>
</div>
)
}
}
const connectCounter = connect(
(state: AppState): StateProps => ({
cnt: state.counter
}),
(dispatch: Dispatch) => bindActionCreators(counterActions, dispatch)
)
export const Counter = connectCounter(CounterInner)
この流れで一点だけヒジョーにイケてないのが export interface CounterActionCreators extends ActionCreatorsMapObject<CounterAction>
の部分。
ActionCreatorsとして定義されているプロパティが例えばkeyof typeof counterActions
などでincrement | decrement
など推論がとれてくれればこんなものは不要になるのだが、現状上記のようなものだとstring | number
となってしまうため、致し方なくCounterActionCreators
を定義している。
ここらへんもっといい方法あったら知りたい