Edited at

JavaScript の switch っぽい何かを再発明してみた

More than 1 year has passed since last update.

y13i/switz: Yet another switch-like control structure.


動機


JS の switch のシンタックスがどうにも好きでない

switch - JavaScript | MDN


  • case が変数のスコープをつくらない


  • break を忘れがちでアレ

  • インデントがしっくりこない


Object Literal で代用しようという記事を時々見かける

個人的にはこちらもあまり馴染めず、自分の書きたいシンタックスから仕様を検討した。


実装

実装は 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 かわいい