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
使用例
使う前に
mport 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("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 かわいい