Actionのunion型
action は「起きたこと」の書類
前のレッスンの action は "increase" という文字だけでした。カートではそうはいきません。「どの商品を入れるのか」「どの行を外すのか」「いくつに変えるのか」という中身が要ります。
そこで action をオブジェクトにします。何が起きたかを type に書き、必要な材料を一緒に載せます。
dispatch({ type: "ADD", item });
dispatch({ type: "REMOVE", id: "1" });
dispatch({ type: "CHANGE_QTY", id: "1", qty: 3 });操作の一覧をユニオン型で書く
3つの action は、載せている材料がそれぞれ違います。ADD には商品そのものが要りますが、REMOVE に要るのは id だけです。これを1つの型で表すのがユニオン型です。TypeScript入門でやった「どれか1つ」の型が、ここで効いてきます。
type CartAction =
| { type: "ADD"; item: Item }
| { type: "REMOVE"; id: string }
| { type: "CHANGE_QTY"; id: string; qty: number };読み方は「CartAction はこの3つのどれか」です。この型が付いていれば、dispatch({ type: "REMOVE" }) のように id を忘れた呼び出しは、その場で怒られます。カートにできることの一覧が、型として1か所に書いてある状態になりました。
switch を書くと絞り込まれる
いいことがもう1つあります。reducer の中で action.type を switch にかけると、TypeScript は case ごとに「これはこの形の action だ」と絞り込みます。
case "ADD":
return [...cart, { item: action.item, qty: 1 }];
case "REMOVE":
return cart.filter((line) => line.item.id !== action.id);ADD の中では action.item が読めて、action.id は無いと言われます。REMOVE の中では逆です。これを絞り込みと呼びます。書ける場所でしか書けないので、材料の取り違えが起きません。
type に string を使うと、この絞り込みは働きません。どの case でも中身が分からないままになり、型を書いた意味がほとんど消えます。
演習
CartAction を3つのユニオン型にして、削除と数量変更の dispatch を書きます。