3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

JavaScriptの関数で引数の必須チェックする方法

Last updated at Posted at 2017-07-14

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]},
};
3
4
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?