y13i/switz: Yet another switch-like control structure.
動機
JS の switch
のシンタックスがどうにも好きでない
- 各
case
が変数のスコープをつくらない -
break
を忘れがちでアレ - インデントがしっくりこない
Object Literal で代用しようという記事を時々見かける
- Replacing switch statements with Object literals
- Rewriting Javascript : Replacing the Switch Statement
個人的にはこちらもあまり馴染めず、自分の書きたいシンタックスから仕様を検討した。
実装
実装は TypeScript 、テストスイートは AVA 。
Switch
, Case
をクラスとして実装し、 switz
という関数はそれらを短い記述で使うためのラッパーとして実装した。
インストール
$ npm install --save switz
使用例
使う前に
import switz from "switz"; // import が使えるならこちら
const switz = require("switz"); // そうでなければ require
基本
const num = Math.ceil(Math.random() * 4);
switz(num, s => {
s.case(1, () => console.log("ワン!"));
s.case(2, () => console.log("ツー!"));
s.case(3, () => console.log("スリー!"));
s.case(4, () => console.log("よ〜ん"));
});
マッチした case
の結果を返すのでより短く書ける
const message = switz(num, s => {
s.case(1, () => "ワン!");
s.case(2, () => "ツー!");
s.case(3, () => "スリー!");
s.case(4, () => "よ〜ん");
});
console.log(message);
正規表現などでのマッチもできる(デフォルトは switch
と同じ ===
での比較)。さらに matcher の戻り値は case handler の引数として与えられる
import switz, {RegexpMatcher} from 'switz';
const longStr = "lo" + (() => {
const n = Math.floor(Math.random() * 30);
let r = "";
for (let i = 0; i < n; i++) r += "o";
return r;
})() + "ng";
console.log(longStr); // => "looooooooooooooooooooong"
const message = switz(longStr, s => {
s.matcher(RegexpMatcher);
s.case(/o{10,}/, match => `yes, ${match[0].length} "o"s.`);
s.default(() => "no");
});
console.log(message); // => "yes, 21 \"o\"s."
TypeScript で使う際は Generics を用いて戻りの型を明示できる
const hoge = switz<string>("foo", s => {
s.case("foo", () => "bar");
s.case("bar", () => "baz");
s.case("fuz", () => 123); // `string` でないのでコンパイル時にエラーになる
});
メソッドチェインで書くこともできる
switz("foo", s => s
.case("foo", () => "bar")
.case("bar", () => "baz")
);
Switch
, Case
クラスを直接使って switch builder 的な使い方もできる
const {Switch, Case} = require("switz");
// or
import {Switch, Case} from "switz";
const subject = Math.floor(Math.random() * 100);
const mySwitch = new Switch(subject);
mySwitch.setMatcher((s, c) => {
const min = c[0];
const max = c[1];
return min <= s && max >= s;
});
mySwitch.addCase(new Case([0, 50], () => "Between 0-50"));
mySwitch.addCase(new Case([50, 100], () => "Between 50-100"));
console.log(mySwitch.evaluate());
感想
- AVA のアサーション が親切すぎて昇天
- npm にパッケージを publish する時のお作法を習得するのにちょっと手間取った
- TypeScript かわいい