変化に強いallow list/deny listをTypeScriptで書く
allow list/deny listを定義すると、後から変更を加えたときlistを修正し忘れることがある。
例えば「果物だけ」を抽出したallow listを定義する。
product.tsconst products = [ "apple", "banana", "orange", "tomato", "eggPlant", "pumpkin",] as const;
type Product = typeof products[number];
const fruits = ["apple", "banana", "orange"];
const filterFruits = (products: Product[]) => products.filter((product) => fruits.includes(product));
console.log(filterFruits([...products]));
後からproductsに果物を追加しても、fruits配列を更新し忘れないようにTypeScriptで対策する。
網羅チェックしたい。
Excludeを使う方法
果物の定義を配列で管理できる。
スマートだけど難しい感じ。
なるべく型定義で頑張る。
product.tsconst products = [ "apple", "banana", "orange", "tomato", "eggPlant", "pumpkin",] as const;
type Product = typeof products[number];
const fruits = ["apple", "banana", "orange"] as const;type Fruit = typeof fruits[number];
const exhaustiveCheck: Fruit = fruits[0] as Exclude< Product, "tomato" | "eggPlant" | "pumpkin">;
const filterFruits = (products: Product[]) => products.filter((product) => (fruits as unknown as string[]).includes(product) );
console.log(filterFruits([...products]));
switch文を使う方法
果物の定義をswitch文で管理する。
イケてないコードのように見えて、実はシンプルな感じ。
型定義を頑張らず、JavaScript側で妥協してる。
型の都合で、filter()ではなくflatMap()を使ったりしてる。
product.tsconst products = [ "apple", "banana", "orange", "tomato", "eggPlant", "pumpkin",] as const;
type Product = typeof products[number];
const filterFruits = (products: Product[]) => products.flatMap((product) => { switch (product) { case "apple": case "banana": case "orange": return [product];
case "tomato": case "eggPlant": case "pumpkin": return [];
default: const exhaustiveCheck: never = product; throw new Error("unreachable"); } });
console.log(filterFruits([...products]));
switch文だいすき。
flatMap()も好き。