2024.10.10
将来は卵1パックの価格が2倍に? 多くの日本人が知らない世界の新潮流、「動物福祉」とは
typescript-fsaに頼らないReact×Redux(全1記事)
リンクをコピー
記事をブックマーク
甲斐田亮一氏:「typescript-fsaに頼らないReact × Redux」というタイトルで始めさせていただきます。僕は日本事務器という会社でフロントエンドエンジニア兼デザイナーをしている甲斐田といいます。
ふだんの業務で、React、TypeScriptでアプリケーションを書いています。
今回話すことですが、最近のTypeScript 2.8以降の機能を使って、React × Reduxの型付けを行っていきます。なるべくReact × Reduxのテンプレートのような書き方を崩さないように、型を組んでいきたいなと思います。
逆に、Flowやtypesctipt-fsaとの書き方や機能面での比較と、TypeScriptの詳しい型システムの解説は、今回行いません。なので、「最近のTypeScriptだとこういうふうに書けるんだよ」くらいの感じに思ってください。
最初にStateful Componentについて。
普通のクラスコンポーネントですよね。型はinterfaceかtype aliasを使ってPropsとStateをそれぞれ定義します。TypeScript3.0からdefaultPropsがサポートされていて、defaultPropsに定義したPropsであれば、宣言時に省略してもnullアサーションがされなくなっています。
型注釈をする場合は別途定義する必要があります。方法としてはこんな感じです。
まずtype aliasでPropsとStateを定義して、React.Componentのあとに型引数としてそれぞれ渡してあげます。InitialStateは別途宣言して、それをそのまま型宣言にも利用します。Readonlyを付けて、setState以外からのアクセスを禁止します。
これを見ていただいたらわかるんですが、リードオンリーって2つ出てきてますよね。これらはそれぞれ別のことをやっていて、上のReadonlyではstate.count=1という代入を、下のreadonlyではstate={count:1}という代入をそれぞれ禁止しています。
続いて、Stateless Functional Component。
React.SFCというもので型を付けていきます。defaultPropsを付ける際は、関数のデフォルト引数を利用して付けていきます。
先月のアップデートで追加されたReact.memoはまだ未対応です(注:発表当時。その後対応された。また、Stateless Functional Componentという名称はなくなり、Function Componentsという名称にまとめられた)。
プルリクは作成されていて、このプルリクで、今あれこれいろいろ議論されています。内容をざっと見たんですが、エキゾチックコンポーネントか、SpecialSFCというのが追加されていたので、このどちらかかなといった印象ですね(注:その後、NamedExoticComponentに決定)。
コードはこんな感じで、先ほどとあまり変わりはないんですが。
Propsを受け取る際に分割して、もし値がなければそれぞれ、これらの値を代入するよということをやっています。
ここからReduxの内容に入ってきます。まずはContainerです。ここでは2つのものに型を付けています。
まず1つ目がmapStateToPropsで、こちらは自分でアプリケーション内で使う必要な型を作成して、それを与えていきます。それで、mapDispatchToPropsは、ReduxにあるDispatchという型を使います。
アプリケーションで下側は定義されていて、ページが3つあります。それぞれcalcPage、hogePage、fooPageがあって、それをインポートしてmapStateToPropsのあとにStateを渡してあげる。mapDispatchToPropsのあとは、Dispatchを渡してあげます。このDispatchは、anyというタイププロパティを持つオブジェクトであれば、なんでも許容されるようになっています。
続いてAction。ここからがメインの話です。
typescript-fsaなりredux-actionsは専用の構文だったりaction creatorを作成していますが、素のTypeScriptだとActionのタイププロパティに「as typeof xxxx」で型を付けていきます。
こうすることで、actiontypeをただのstringではなくて、文字列リテラル型の型を付与することができます。コードはこんな感じになっていて、もとのテンプレートの書き方をほぼほぼ崩さずに書けているんじゃないのかなと思います。
そしてReducerで、先ほど作成したActionの型を紐づけていきます。
ここからがっつりTypeScriptの機能を使っていて、まずConditional typesのReturnTypeというものを使って、そのReducer内で受け取るActionを定義します。そして、Tagged union typesというもので各Actionが持つプロパティを絞り込みます。最後にnever型を使って、そのReducer内で受け取るActionの定義漏れだったり、タイポがないかをチェックします。
だいぶ小さくてすいません。そもそもこれは何してるかなんですけど、このReturnTypeは関数を受け取るようになっていて、その関数の戻り値の型を抽出するっていうことをやっています。
そして、それをunion typesで結合しています。
フォーカスしてみるとこのように、3つのActionが受け取ることをTypeScript側が認識することができています。
INCREMENT、DECREMENTはtypeだけを持っていて、CALCだったらpayloadを持っているとちゃんと認識しています。payloadは中身がnum1、num2を持っていて、それは型がnumberだということもちゃんと認識してくれています。
これをswitch文で、tagged union typesでActionの絞り込みを行っていきます。
そして、TypeScript側で「このActionだったらこの型を持っている」「payloadを持っている」と認識しているので、CALC内でpayloadに対して直接アクセスすることができています。
最後にnever型。
default部で定義して、もしこちらが予期していないActionが来た場合は、このnever型のアンダーバーの中にActionが流れ込みます。例えばこのDECREMENT定数をコメントアウトすると、例えばこのDECREMENTケースをコメントアウトすると、このReducerはINCREMENT、DECREMENT、CALCという3つのActionを期待しているのに、DECREMENTの受け取り口がなくなるので、DECREMENTがdefault区に流れ込みます。
そこでこのアンダーバーに、never型にアサインされてしまうので、TypeScriptがエラーを発動してしまう。なのでここで、その次が実行できなくなってしまいます。こうすることでReducerを型安全に、かつ型の恩恵を受け取りつつ書くことができます。
ただ、気を付けることがあります。
受け取るActionが1つのときは、このnever型が使えないんですよね。なのでnever型を消すか、普通にif文で書けば書けるようになります。JavaScriptだと、switch文に入る前にActionを分割代入で先に取ってから、switch文でpayloadのアクセスができるようになっていたんですが、それは今回の書き方ではできません。
最後、Middleware。
今回Middlewareは、素のMiddlewareで作りました。store、next、actionにそれぞれ型を付けます。actionの絞り込みには先ほどのConditional typesを利用できます。今回は書き方だけ紹介します。実際にアプリケーションで使うときは素のMiddlewareではなくて、sagaなりthunk、observablesを使うのがいいです。
コードとしてはこんなふうになっています。
storeにMiddlewareAPIというのを渡してあげて、Dispatchとそのアプリケーションで使うstoreを渡してあげます。ReduxはMiddlewareという型定義をちゃんと定義しているんですが、その場合はactionの型推論がanyになっているので。actionの型推論が一切なくなります。なので僕は書くとしたらこういうふうに書きます。
まとめです。
2.8以降の機能であれば素のTypeScriptでもいい感じに型を付けていけてるんじゃないのかなと思います。僕はもともとFlowを使っていて、TypeScriptって敬遠してたんですね。というのが、サードパーティを使わないといけないし、書き方が大きく崩れるなと思っていて、やってなかったんですが。
最近のTypeScriptだったらそのテンプレートを崩さずに、さらに型の恩恵にあやかりつつ書くことができるので、いいんじゃないのかなと思います。なのでFlowとかで迷っている人がいれば、TypeScriptを検討してみてもいいんじゃないのかなと思います。以上です、ご清聴ありがとうございました。
(会場拍手)
関連タグ:
2024.11.13
週3日働いて年収2,000万稼ぐ元印刷屋のおじさん 好きなことだけして楽に稼ぐ3つのパターン
2024.11.11
自分の「本質的な才能」が見つかる一番簡単な質問 他者から「すごい」と思われても意外と気づかないのが才能
2024.11.13
“退職者が出た時の会社の対応”を従業員は見ている 離職防止策の前に見つめ直したい、部下との向き合い方
2024.11.12
自分の人生にプラスに働く「イライラ」は才能 自分の強みや才能につながる“良いイライラ”を見分けるポイント
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
2024.11.11
気づいたら借金、倒産して身ぐるみを剥がされる経営者 起業に「立派な動機」を求められる恐ろしさ
2024.11.11
「退職代行」を使われた管理職の本音と葛藤 メディアで話題、利用者が右肩上がり…企業が置かれている現状とは
2024.11.18
20名の会社でGoogleの採用を真似するのはもったいない 人手不足の時代における「脱能力主義」のヒント
2024.11.12
先週まで元気だったのに、突然辞める「びっくり退職」 退職代行サービスの影響も?上司と部下の“すれ違い”が起きる原因
2024.11.14
よってたかってハイリスクのビジネスモデルに仕立て上げるステークホルダー 「社会的理由」が求められる時代の起業戦略
2024.11.13
週3日働いて年収2,000万稼ぐ元印刷屋のおじさん 好きなことだけして楽に稼ぐ3つのパターン
2024.11.11
自分の「本質的な才能」が見つかる一番簡単な質問 他者から「すごい」と思われても意外と気づかないのが才能
2024.11.13
“退職者が出た時の会社の対応”を従業員は見ている 離職防止策の前に見つめ直したい、部下との向き合い方
2024.11.12
自分の人生にプラスに働く「イライラ」は才能 自分の強みや才能につながる“良いイライラ”を見分けるポイント
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
2024.11.11
気づいたら借金、倒産して身ぐるみを剥がされる経営者 起業に「立派な動機」を求められる恐ろしさ
2024.11.11
「退職代行」を使われた管理職の本音と葛藤 メディアで話題、利用者が右肩上がり…企業が置かれている現状とは
2024.11.18
20名の会社でGoogleの採用を真似するのはもったいない 人手不足の時代における「脱能力主義」のヒント
2024.11.12
先週まで元気だったのに、突然辞める「びっくり退職」 退職代行サービスの影響も?上司と部下の“すれ違い”が起きる原因
2024.11.14
よってたかってハイリスクのビジネスモデルに仕立て上げるステークホルダー 「社会的理由」が求められる時代の起業戦略