[EffectiveJavaScript輪読会8]過剰な型強制を防ごう

GAS

どうも。つじけ(tsujikenzo)です。このシリーズでは2021年8月からスタートしました「ノンプロ研EffectiveJavaScript輪読会」についてお送りします。

前回のおさらい

前回は、「柔軟なインターフェイスのために、構造的な型付けを使う」をお届けしました。

[EffectiveJavaScript輪読会8]配列と「配列のようなもの」を区別しよう
どうも。つじけ(tsujikenzo)です。このシリーズでは2021年8月からスタートしました「ノンプロ研EffectiveJavaScript輪読会」についてお送りします。前回のおさらい前回は、「柔軟なインターフェイスの...

今日は7回目で、「過剰な型強制を防ごう」をお届けします。

テキスト第6章「ライブラリとAPI設計」の項目59に対応しています。

今日のアジェンダ

  • 暗黙の型変換
  • 防御的プログラミング
  • Mix-in

暗黙の型変換

一瞬だけおさらいですが、JavaScriptには、暗黙の型変換があります。

他の言語を学ぶ方からしたら、とんでもないことです。

function myFunction8_59_01() {

  const square = x => x * x;
  console.log(square('3')); //9

}

前回のクラスは、文字列を引数で受け取ることを想定していませんでした。

function myFunction8_59_02() {


  /** クラスBitVector */
  class BitVector {

    constructor() {
      this.bit = [];
    }

    /** bitを格納するメソッド */
    enable(x) {

      if (typeof x === 'number') { //常に真
        this.enableBit(x);
      } else {
        for (let i = 0; i < x.length; i++) {
          this.enableBit(x[i]);
        }
      }

    }

    /** ビットを格納するメソッド */
    enableBit(x) {
      this.bit.push(x);
    }

    /** ビット配列に引数があるか返すメソッド */
    bitAt(x) {
      return this.bit.includes(x) ? 1 : 0;
    }


  }

  const bits = new BitVector();
  bits.enable('3'); //数値型?配列?配列のようなもの?

  console.log(bits.bitAt(3)); // => 0
  console.log(bits.bit); // => ['3']

}

防御的プログラミング

予期せぬ引数や型が渡されたときに、例外をはくように、あらかじめバグになりそうな芽を早めにつぶすことを「防御的プログラミング」と呼びます。

メソッドの引数に、条件分岐を設定することで、型のチェックを行います。

function myFunction8_59_03() {


  /** クラスBitVector */
  class BitVector {

    constructor() {
      this.bit = [];
    }

    /** bitを格納するメソッド */
    enable(x) {

      if (typeof x === 'number') {
        this.enableBit(x);
      } else if (typeof x === 'object' && x) {
        for (let i = 0; i < x.length; i++) {
          this.enableBit(x[i]);
        }
      } else {
        throw new TypeError('expected number or array-like');
        //数値または配列のようなオブジェクトを期待している
      }
    }


    /** ビットを格納するメソッド */
    enableBit(x) {
      this.bit.push(x);
    }

    /** ビット配列に引数があるか返すメソッド */
    bitAt(x) {
      return this.bit.includes(x) ? 1 : 0;
    }

  }

  const bits = new BitVector();
  bits.enable('3'); //TypeError: expected number or array-like

}

Mix-in

JavaScriptには、複数のスーパークラスから継承する多重継承は実装できませんが、代替えとしてMix-in(ミックスイン)という手法があります。

今回は、型のチェックをするオブジェクトを作成し、Mix-inで実装したコードをご紹介します。

function myFunction59_04() {

  const guard = {

    guard(x) {
      if (!this.test(x)) {
        throw new TypeError('excepted' + this);
      }
    },

    or(other) {
      const result = Object.create(guard);
      const self = this;
      result.test = function (x) {
        return self.test(x) || other.test(x);
      };
      const destriction = this + ' or ' + other;
      result.toString = function () {
        return destriction;
      };
      return result;
    }

  };

  /** Unit32クラス */
  class Uint32 {

    test(x) {
      return typeof x === 'number' && x === (x >>> 0);
    }

    toString() {
      return 'uint32';
    }

  }

  //mix-in処理
  Object.assign(Uint32.prototype, guard);

  const uint32 = new Uint32();

  /** ArrayLikeクラス */
  class ArrayLike {

    test(x) {
      return typeof x === 'object' && x && uint32.test(x.length);
    }

    toString() {
      return 'array-like Object';
    }
  }

  //mix-in処理
  Object.assign(ArrayLike.prototype, guard);

  const arrayLike = new ArrayLike();


  /** BitVectorクラス */
  class BitVector {

    constructor() {
      this.bit = [];
    }

    /** bitを格納するメソッド */
    enable(x) {
      uint32.or(arrayLike).guard(x);
      if (uint32.test(x)) {
        this.enableBit(x);
      } else if (arrayLike.test(x)) {
        for (const n of x) {
          this.enableBit(n);
        }
      }
    }

    /** ビットを格納するメソッド */
    enableBit(x) {
      this.bit.push(x);
    }

    /** ビット配列に引数があるか返すメソッド */
    bitAt(x) {
      const result = this.bitAt.includes(x);
      return result ? true : false;
    }
  }

  const bits = new BitVector();

  bits.enable([4, 3]);
  console.log(bits); // => { bit: [ 4, 3 ] }

  bits.enable(5);
  console.log(bits); // => { bit: [ 4, 3, 5 ] }

}

Mix-inについては、別途ブログ化します。

まとめ

以上で、「過剰な型強制を防ごう」をお届けしました。

一旦、これで輪読会は終了です。

みなさん、長い間お疲れ様でした。

参考資料

このシリーズの目次

タイトルとURLをコピーしました