2024.12.10
“放置系”なのにサイバー攻撃を監視・検知、「統合ログ管理ツール」とは 最先端のログ管理体制を実現する方法
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.12.10
メールのラリー回数でわかる「評価されない人」の特徴 職場での評価を下げる行動5選
2024.12.09
10点満点中7点の部下に言うべきこと 部下を育成できない上司の特徴トップ5
2024.12.09
国内の有名ホテルでは、マグロ丼がなんと1杯「24,000円」 「良いものをより安く」を追いすぎた日本にとって値上げが重要な理由
2024.12.12
会議で発言しやすくなる「心理的安全性」を高めるには ファシリテーションがうまい人の3つの条件
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
2024.12.10
職場であえて「不機嫌」を出したほうがいいタイプ NOと言えない人のための人間関係をラクにするヒント
2024.12.12
今までとこれからで、エンジニアに求められる「スキル」の違い AI時代のエンジニアの未来と生存戦略のカギとは
PR | 2024.11.26
なぜ電話営業はなくならない?その要因は「属人化」 通話内容をデータ化するZoomのクラウドサービス活用術
PR | 2024.11.22
「闇雲なAI導入」から脱却せよ Zoom・パーソル・THE GUILD幹部が語る、従業員と顧客体験を高めるAI戦略の要諦
2024.12.11
大企業への転職前に感じた、「なんか違うかも」の違和感の正体 「親が喜ぶ」「モテそう」ではない、自分の判断基準を持つカギ