React, Reduxのお型付け
あとでブログに書き直す
コンセプト
楽に
安全に
できるだけ外部ライブラリに頼らずに
推論が利く部分は端折ったり
実はハンズオンです
$ git clone -b all-any-type https://github.com/mrsekut/react-redux-with-typescript-handson.git
$ cd react-redux-with-typescript-handson
$ npm i
TypeScriptのコンパイル
$ npm run tsc
起動(別のターミナルを開いて)
$ npm start
軽く準備をしながらTypeScriptのキホンをお聞きください
TypeScriptのキホン
とりあえずは以下のものを知っておけば耐える
number: 42
string: "hoge"
boolean: true
any
なんでもいけるやつ
型を自作する
微妙な差はあるが、どっちでもいける
type
型に別名を付けるもの
code:ts
// name, ageを持ったオブジェクトに「Person」と命名
type Person = {
name: string;
age: number;
}
interface
クラスやオブジェクトの仕様を決めるもの
code:ts
interface Person {
name: string;
age: number;
}
差を知りたい人は
ちなみにVSCodeでは、
typeはhoverすると中身が見える
https://gyazo.com/c3c8b20d439ea0b9068e1f9c5efc4c63
interfaceは見えない
https://gyazo.com/8a30db5f64880319f6b24c7019f6b252
関数の型の書き方
複数の書き方があるが今回は以下に統一
(変数名: 引数の型) => 戻り値の型
code:ts
type Hoge = {
// number型の引数を一つ取って、戻り値のない関数
addFunc: (num: number) => void;
}
Reactでは、
自作のコンポーネントに型を付けることで、必須のプロパティなどを強制できる
後で見ます
型定義
自作する
ライブラリとして用意されているものを使う
ライブラリに一緒に入っているものもあれば、別途入れる必要があるものもある
@types/reactみたいなやつ
VSCodeは割といい感じに補完してくれる
赤線の上でcmd-.(⌘+ドット)
推論される型を明示してくれたり
他のファイルからimportしてくれたり
ここからハンズオン
React, Reduxの小さなプロジェクトに型を付けていく
このままでも動く
スタート時はすべてanyになっている
「+」と「-」があるだけのカウンター
普通この規模ならわざわざReduxとか使わないけど..
Reactに型を付ける
Presentational Componentに型を付ける
src/components以下
stateを持ってないやつ
プロジェクトの中の大半のコンポーネントがこれ
Counter/index.tsx
src/components/Counter/index.tsx
数値を表示するだけのコンポーネント
こんな感じ
https://gyazo.com/5855c7fde8dd2ed70219021da558216a
typeかinterfaceを使って、
CounterPropsで、コンポーネントに必要なプロパティを定義
React.FC<コンポーネントの型>で指定
Function Componentの型のgenericsを使う
propsは常にreadonlyなので、Readonlyで縛る流派もある(っぽい)
以下の2つは全く同じ意味
後者の方が楽
code:ts
// 一つ一つに、readonlyと書くか、
type CounterProps {
readonly num: number;
}
// Readonly<>型を使うか
React.FC<Readonly<CounterProps>>
Button/index.tsx
src/components/Button/index.tsx
ボタンのコンポーネント
https://gyazo.com/7300a6904ddef2e2f7de6542b786c184
onClickは関数
?は「任意のプロパティである」ことを示す
childrenはなくても推論されるが、自作型の中に書くことで、強制できる
Reactの用意している似たようなカタガタ
React.ReactNode
ReactElement、 Fragment、 Portals、primitiveなJSな型を含む
React.ReactChild
ReactElementもしくはstring もしくは number
React.ReactElement
divとかpとかみたいな仮想DOMを表す
HTMLElementに対応するReactの型
コンポーネントに型を付けて何が嬉しいか
コンポーネント作者の意図しない使われ方をするとコンパイルエラーになる
→共同開発時などで楽
https://gyazo.com/babbf37ff85d239ac0c370b54c6d0b46
Reduxに型を付ける
Reduxに関しては、型付けのためのライブラリが乱立してる
etc.
今回は、何も使わずにできる方法を紹介
Actionに型をつける
src/modules/module.ts
ボタンを押した時に実行される関数
https://gyazo.com/ba458bfa48777749dd7974c0ec11cce3
asはキャスト
actionのための型を別途、定義しなくても良い
Module全体のActionの型
src/modules/module.ts
このmodule内のすべてのactionのUnion typesとして作成
https://gyazo.com/d616ea334cbd85668d17515d110b9a99
typeof T
Tの型
ReturnType<T>
Tが関数の型なら、その返り値の型
Conditional Typesを使って定義されている
type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;
initialStateに型を付ける
src/modules/module.ts
global stateの初期値
https://gyazo.com/545b516e30cef7e7f354abcf6bbb7bc8
readonlyを付けとくと、より堅い
型だけに()
Reducerに型を付ける
src/modules/module.ts
reduxが用意しているReducer
先ほど定義したactionとstateの型を使う
https://gyazo.com/c12027ced5c76ec535d3a3676e09f1f2
never型
neverにはneverしか入らない
→Union Typesに対するswitch文に使うと、網羅漏れを防げる
Storeに型を付ける
src/store.tsx
各modulesで作った型をまとめる
https://gyazo.com/50303b5cb5c0ba886b4534a9284bcadd
moduleが複数のときに対応するためにこんな書き方をしている
Containerに型をつける
src/containers/index.tsx
reduxと接続しているコンポーネント
プロジェクトのルートに近い部分がこれ
mapStateToPropsとmapDispatchToPropsに型をつける
https://gyazo.com/34a78ace126676e5a678182d20c6a3a5
ContainerのPropsの型を作る
ここでもReturnType<T>を使う
https://gyazo.com/0a0620895023c0b0cbbdbf212a436b71
Containerに型を実装
一番最初に紹介したStateless Componentと同じ
今までは、classだったけど関数で書く
React.FC<T>を使う
https://gyazo.com/d7f9e9879fd6a3d84fc7f43aee37e973
おわり
だいたい、こんな感じ
まだ、middlewareをどうするかなども残っている
参考