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

使用例

使う前に

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 かわいい

元記事はこちら

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