JavaScriptにおける関数の引数のチェックについての考察。
まずは必須チェックを。
必須のチェック
required_01.js
// 必須チェック関数
function required(key) {
throw new Error(`${key}は必須です。`);
}
// とある関数
function sayHello(name=required("名前")) {
console.log(`ハロー${name}`)
}
try {
sayHello("JavaScript"); // ハローJavaScript
sayHello();
}
catch(e) {
console.log(e.message); // 名前は必須です。
};
カスタムErrorを投げた方が良さげなので、書きなおしてみる。
required_02.js
// RequiredErrorというカスタムエラーを作る
function RequiredError(message="必須エラー") {
this.message = message;
let last_part = new Error().stack.match(/[^\s]+$/);
this.stack = `${this.name} at ${last_part}`;
}
Object.setPrototypeOf(RequiredError, Error);
RequiredError.prototype = Object.create(Error.prototype);
RequiredError.prototype.name = "RequiredError";
RequiredError.prototype.message = "";
RequiredError.prototype.constructor = RequiredError;
// 必須チェック関数(改:RequiredErrorを投げる)
function required(key) {
//throw new Error(`${key}は必須です。`);
throw new RequiredError(`${key}は必須です。`);
}
try {
sayHello();
}
catch(e) {
if ( e instanceof RequiredError ) {
console.log(e.message); // 名前は必須です。
}
};
書いてはみたものの問題が浮上。
この方法は、引数のデフォルト値を利用してrequired()を呼び出しているので、
例えば型のチェックなどは、このタイミングでは出来ない。
なので、
他の引数チェック系関数を作ったとき、その使い方に統一性が失われてしまう。
また何より、必須の引数が3つもあると、もう見辛い。
素直に下のような書き方のが良いのかもしれない。
required_03.js
// 必須チェック関数
function required(val) {
if ( valの必須チェック ) {
throw new RequiredError();
}
return val;
}
// とある関数
//function sayHello(name=required("名前")) {
function sayHello(name) {
name = required(name);
console.log(`ハロー${name}`)
}
引数の明示と同時に、
型や必須、デフォ値の面倒みてくれるような書き方が望ましいんだけどねぇ。
型のチェックって意外に難しいのでオレオレじゃなくて、
偉い人が作ってくれたようなの、ないの?
ん、typescript使え?
引数をチェックするクラス(追記)
コメントを受けて、引数のチェッククラス(案)を書いてみました。
args-check.js
class ArgsCheck {
get val() { return this._val; }
constructor(val, name="") {
this._val = val;
this.name = name;
}
required() {
if ( !this.val )
throw Error(`エラー:${this.name} 必須です。`);
return this;
}
isString() {
if ( typeof(this.val) != "string" )
throw Error(`エラー:${this.name} stringじゃないよ。`);
return this;
}
length(...size) {
if ( size.length == 1 ) {
let max = size[0];
if ( this.val.length > max )
throw Error(`エラー:${this.name} 文字の長さは${max}までです。`);
}
else if ( size.length == 2 ) {
let min = size[0];
let max = size[1];
if ( this.val.length < min )
throw Error(`エラー:${this.name} 文字の長さは${min}以上です。`);
if ( this.val.length > max )
throw Error(`エラー:${this.name} 文字の長さは${max}までです。`);
}
else {
// スルーするか、エラーか警告出すか
}
return this;
}
}
// とある関数
function sayHello(name) {
name = new ArgsCheck(name, "名前").required().isString().length(10,20).val;
console.log(`ハロー${name}`)
}
try {
sayHello("JavaScript"); // ハローJavaScript
sayHello("Perl");
}
catch(e) {
console.log(e.message); // エラー:名前 文字の長さは10以上です。
};
チェック用のメソッドを育てていけば、まともになるのかなぁ。
も少し理想を言うと、下記のような定義を用意しておいて、
内部で、上で書いたチェック関数を使うような仕組みが望ましいですかね。
let rule = {
name : {required:1, type:"string", length:[10,20]},
};