変化に強いallow list/deny listをTypeScriptで書く

変化に強いallow list/deny listをTypeScriptで書く

allow list/deny listを定義すると、後から変更を加えたときlistを修正し忘れることがある。
例えば「果物だけ」を抽出したallow listを定義する。

product.ts
Copied!
const 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.ts
Copied!
const 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.ts
Copied!
const 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文だいすき。hata6502
flatMap()も好き。
Powered by Helpfeel