Non TypeScriptスタックで型のバリデーションをする機構を01で実装してみる

こんにちは(๑╹ω╹๑ )

↓ つい先日投稿した記事を多くの方に読んで頂けてとても嬉しいです（語彙力.....

[「Vercel + freenom + Getform.io」構成で「爆速 + 管理費完全無料」のポートフォリオをGatsbyJSで作ったら幸福度が高まった話](https://qiita.com/huuya/items/a52609d5407ad873eaa2)


今日はJavascript（主にTypeScriptスタックの無い環境）で型のバリデーションをする機構の実装について紹介します👼

少し特殊な書き方をしますが、可変メソッドを多用する立ち回りで実装できます😄

## 可変メソッドとは

- メソッド名からメソッドを評価する

ことです。

PHPでは、

- 可変変数
- 可変関数
- 可変クラス

等できるかと思いますが、
それに近しい立ち回りです🙆‍♂️

以前、[PHPの言語仕様の理解を深める特殊な立ち回り](https://qiita.com/huuya/items/f9bedb6b600c55613c4b#%E5%8F%AF%E5%A4%89%E5%A4%89%E6%95%B0)でも紹介した下記のPHPコードを見てください👨‍💻

```php
$orange_price = "300円";

$var = "orange_price";

echo $$var; //300円
```

↑のような立ち回りをJavascriptのメソッドでもしよう💪ということです！

### 実は簡単🤔 JavaScriptの可変メソッド😊

まず↓を眺めましょう🙌

```js
console.log("Hoge")
```

言語仕様まで追っている訳ではありませんが、
`console`というクラスの`log`という`@staticmethod`に`Hoge`というString型の値を渡して、
`console.log("Hoge")`を評価させていますよね🐥

`console`はオブジェクトだということも

分かるので

ということは、、、、！

```js
console['log']("Hoge");
```

も同様に評価されるということです😲

よって、`log`という`String型`の値を定義しておくと、、、、


```js
const Var = 'log';

console[Var]("Hoge");
```

のように`可変メソッド`で立ち回る事ができます🧐

### 可変メソッドをコールするメソッドを書いてみる

PHPの[call_user_func_array](https://www.php.net/manual/ja/function.call-user-func-array.php)をJavascriptで実装してみます。
※メソッドのみ

↓書いてみました！

```js

class Hoge {

    displaymMssage( mssage ){
        console.log(mssage);
    }

    callUserMethodArray(
        methodName,
        var
    ){
        this[methodName](var);
    }

}

const hoge = new Hoge;
hoge.callUserMethodArray(
    'displaymMssage',
    'Hoge!!!'
);
// => Hoge!!!
```

もちろん許容する引数の数は、
必ずしも1つでは無いので、
引数が複数である場合も考慮して書いてみました🥺

```js

class Hoge {

    displaymMssage( mssage1, mssage2 ){
        console.log(mssage1);
        console.log(mssage2);
    }

    callUserMethodArray( methodName, vars ){
        this[methodName].apply( this, vars );
    }

}

const hoge = new Hoge;
hoge.callUserMethodArray(
    'displaymMssage',
    [ 'Hoge!!!', 'Foo!!!' ]
);
// => Hoge!!!
// => Foo!!!

```

`apply`で立ち回ると出来ますね！

これに可変プロパティを用いることも簡単です

```js
class Hoge {

    constructor(){
      this.mssage1 = "Hoge!!!";
      this.mssage2 = "Foo!!!";
    }

    displaymMssage( var1, var2 ){
        console.log( this[var1] );
        console.log( this[var2] );
    }

    callUserMethodArray( methodName, vars ){
        this[methodName].apply( this, vars );
    }

}

const hoge = new Hoge;
hoge.callUserMethodArray(
    'displaymMssage',
    [ 'mssage1', 'mssage2' ]
);
// => Hoge!!!
// => Foo!!!
```


## 動的に型のバリデーションを行う

動的に可変メソッドを用いて立ち回ると、
Non TypeScriptスタックでも簡単にバリデーションチェックを行う機構を実装できます！

```js

class Hoge {

    constructor(){
      this.validationRules = {
          displaymMssage: [
              "string",
              "boolean",
              "number",
          ]
      };
    }

    displaymMssage( mssage1, mssage2, mssage3 ){
        console.log(mssage1);
        console.log(mssage2);
        console.log(mssage3);
    }

    validationArguments( methodName, vars ){
      const rules = this.validationRules[methodName];
      return rules.map(
        ( rule, key ) => {
          return (
            typeof vars[key] === rule
            ? null
            : rule
          );
        }
      ).filter(Boolean);
    }

    callUserMethodArray( methodName, vars ){
        const validationResult = this.validationArguments( methodName, vars );
        if(validationResult.length > 0){
          const errorTypes = validationResult.join(" and ");
          return `Failed to validate the argument. Pass the correct ${errorTypes}.`;
        }
        this[methodName].apply( this, vars );
    }

}

const hoge = new Hoge;
const result = hoge.callUserMethodArray(
    'displaymMssage',
    [ 'Hoge!!!', 10, true ]
);

console.log(result);
// => Failed to validate the argument. Pass the correct boolean and number.

```

↑です！

簡単に解説をします🤗

```js
this.validationRules = {
    displaymMssage: [
        "string",
        "boolean",
        "number",
    ]
};
```

`Hoge`クラスの`constructor`で定義をした↑は、
`displaymMssage`というメソッドで許可する`型`を
引数順に文字列で定義しています。
※ サンプルコード内ではstring型、number型、boolean型の順番で実行させようとしています。

`validationArguments`メソッドでは、

```js
const rules = this.validationRules[methodName];
```

↑で該当のルールを`rules`で定義しています。

```js
return rules.map(
    ( rule, key ) => {
        return (
            typeof vars[key] === rule
            ? null
            : rule
        );
    }
).filter(Boolean);
```

↑では、事前に定義した型と`displaymMssage`メソッドを実行するために渡された引数の型が
同一であるか確認しています。
その後、想定していない型であれば、
型名をstring型(`rule`)で返却します。
`map`によって返却された配列を、

```js
.filter(Boolean);
```

で`null`を除去しています。
ここで`null`を除去出来るのは、

```js
Boolean(null)
```

が`false`であるためです。

そして、`callUserMethodArray`メソッド内では、
バリデーションエラーがあった場合に、
エラー分を`return`しています。

```js
.filter(Boolean);
```

で`null`を除去していたので、
バリデーションエラーがあるのは`validationArguments`から返却された配列が空で無い時です。

これらの処理により、型のバリデーションに失敗した場合は想定のメソッドを評価させること無く、
例外エラーを差し込むことが出来ます🤗

```js
[ 'Hoge!!!', true, 10 ]
```

を引数とすると、

```
// => Hoge!!!
// => true
// => 10
```

となって期待する処理の結果を得ることが出来ます。

## まとめ

型のバリデーションを行うために、
ワンクッションメソッドを挟むことになり少し手間です。

どうしても、
「Non TypeScriptでないといけない😭
でも型のバリデーションはきちんと行いたい😭😭」
という場合には活用できそうですね😄