1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TypeScriptで柔軟なバリデーションクラスを実装する

Last updated at Posted at 2025-02-25

フォーム入力値のバリデーションを簡単に行うための Validation クラスをTypeScriptで実装しました。
このクラスは共通のバリデーションルールをサポートし、カスタムルールも定義できます。


バリデーション仕様

1. 共通バリデーション

  • required: 必須入力
  • maxLength: 最大文字数
  • minLength: 最小文字数
  • rangeLength: 文字数の範囲
  • min / max: 数値の範囲
  • between: 数値の最小値~最大値
  • pattern: 正規表現チェック
  • equalsTo: 他の入力値と一致

2. 特定の入力タイプ向けバリデーション

  • email: メールアドレス形式
  • date: YYYY/MM/DD の日付形式
  • alphaNumeric: 英数字のみ
  • alpha: 英字のみ
  • numeric: 数字のみ
  • checkboxMin / checkboxMax: チェックボックスの選択数制限

3. カスタムバリデーション

以下のように定義可能:

const customValidators = {
  startsWithA: (value: string) => {
    return value.startsWith("A")
      ? { valid: true }
      : { valid: false, message: "Value must start with 'A'.\n'A' で始まる必要があります。" };
  },
};

TypeScript Validation クラス

class Validation {
  private customValidators: { [key: string]: (value: any, ruleValue: any) => { valid: boolean; message?: string } };
  private errorMessages: { [key: string]: string } = {
    required: "[{field}] is required.\n[{field}] は必須です。",
    maxLength: "[{field}] must be at most {max} characters.\n[{field}] の最大文字数は {max} です。",
    minLength: "[{field}] must be at least {min} characters.\n[{field}] の最小文字数は {min} です。",
    rangeLength: "[{field}] must be between {min} and {max} characters.\n[{field}] の文字数は {min}~{max} です。",
    min: "[{field}] must be at least {min}.\n[{field}] の最小値は {min} です。",
    max: "[{field}] must be at most {max}.\n[{field}] の最大値は {max} です。",
    between: "[{field}] must be between {min} and {max}.\n[{field}] は {min}~{max} の範囲である必要があります。",
    pattern: "[{field}] format is invalid.\n[{field}] の形式が正しくありません。",
    equalsTo: "[{field}] must match {targetField}.\n[{field}] は {targetField} と一致する必要があります。",
    email: "[{field}] must be a valid email address.\n[{field}] は有効なメールアドレスである必要があります。",
    date: "[{field}] must be a valid date (YYYY/MM/DD).\n[{field}] は YYYY/MM/DD 形式の日付である必要があります。",
    alphaNumeric: "[{field}] must contain only letters and numbers.\n[{field}] は英数字のみ使用できます。",
    alpha: "[{field}] must contain only letters.\n[{field}] は英字のみ使用できます。",
    numeric: "[{field}] must contain only numbers.\n[{field}] は数字のみ使用できます。",
    checkboxMin: "[{field}] must select at least {min} options.\n[{field}] は最低 {min} 個選択してください。",
    checkboxMax: "[{field}] must select at most {max} options.\n[{field}] は最大 {max} 個まで選択できます。",
  };

  constructor(customValidators: { [key: string]: (value: any, ruleValue: any) => { valid: boolean; message?: string } } = {}) {
    this.customValidators = customValidators;
  }

  /**
   * 指定されたフィールドの値をバリデーションする
   * @param {string} fieldName - 検証するフィールド名
   * @param {string | Array} value - 入力値(チェックボックスの場合は配列)
   * @param {Object} rules - バリデーションルールのオブジェクト
   * @return {Object} - `valid` (boolean), `errors` (エラーメッセージの配列)
   */
  validate(fieldName: string, value: any, rules: { [key: string]: any }) {
    const errors: string[] = [];

    for (const rule in rules) {
      const ruleValue = rules[rule];
      let valid = true;
      let errorMessage = this.errorMessages[rule]?.replace("{field}", fieldName);

      switch (rule) {
        case "required":
          valid = value !== undefined && value !== null && value !== "";
          break;
        case "maxLength":
          valid = typeof value === "string" && value.length <= ruleValue;
          errorMessage = errorMessage.replace("{max}", ruleValue);
          break;
        case "minLength":
          valid = typeof value === "string" && value.length >= ruleValue;
          errorMessage = errorMessage.replace("{min}", ruleValue);
          break;
        case "rangeLength":
          valid = typeof value === "string" && value.length >= ruleValue[0] && value.length <= ruleValue[1];
          errorMessage = errorMessage.replace("{min}", ruleValue[0]).replace("{max}", ruleValue[1]);
          break;
        case "min":
          valid = parseFloat(value) >= ruleValue;
          errorMessage = errorMessage.replace("{min}", ruleValue);
          break;
        case "max":
          valid = parseFloat(value) <= ruleValue;
          errorMessage = errorMessage.replace("{max}", ruleValue);
          break;
        case "between":
          valid = parseFloat(value) >= ruleValue[0] && parseFloat(value) <= ruleValue[1];
          errorMessage = errorMessage.replace("{min}", ruleValue[0]).replace("{max}", ruleValue[1]);
          break;
        case "pattern":
          valid = new RegExp(ruleValue).test(value);
          break;
        case "equalsTo":
          valid = value === ruleValue;
          errorMessage = errorMessage.replace("{targetField}", ruleValue);
          break;
        case "email":
          valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
          break;
        case "date":
          valid = /^\d{4}\/\d{2}\/\d{2}$/.test(value);
          break;
        case "alphaNumeric":
          valid = /^[a-zA-Z0-9]+$/.test(value);
          break;
        case "alpha":
          valid = /^[a-zA-Z]+$/.test(value);
          break;
        case "numeric":
          valid = /^[0-9]+$/.test(value);
          break;
        case "checkboxMin":
          valid = Array.isArray(value) && value.length >= ruleValue;
          errorMessage = errorMessage.replace("{min}", ruleValue);
          break;
        case "checkboxMax":
          valid = Array.isArray(value) && value.length <= ruleValue;
          errorMessage = errorMessage.replace("{max}", ruleValue);
          break;
        default:
          if (this.customValidators[rule]) {
            const result = this.customValidators[rule](value, ruleValue);
            valid = result.valid;
            if (!valid && result.message) errorMessage = result.message;
          }
      }

      if (!valid && errorMessage) errors.push(errorMessage);
    }

    return { valid: errors.length === 0, errors };
  }

  /**
   * 複数のフィールドをまとめてバリデーションする
   * @param {Object} fields - フィールドごとの入力値とルールのオブジェクト
   * @return {Object} - `valid` (boolean), `results` (各フィールドのバリデーション結果)
   */
  validateAll(fields: { [key: string]: { value: any; rules: { [key: string]: any } } }) {
    let allValid = true;
    const results: { [key: string]: { valid: boolean; errors: string[] } } = {};

    for (const field in fields) {
      const { value, rules } = fields[field];
      const result = this.validate(field, value, rules);
      results[field] = result;
      if (!results[field].valid) allValid = false;
    }

    return { valid: allValid, results };
  }
}


使い方

const validator = new Validation({
  startsWithA: (value) => {
    return value.startsWith("A")
      ? { valid: true }
      : { valid: false, message: "Value must start with 'A'.\n'A' で始まる必要があります。" };
  },
});

const result = validator.validate("username", "Abe", { required: true, minLength: 3 });
console.log(result);

const allResult = validator.validateAll({
  email: { value: "test@example.com", rules: { required: true, email: true } },
});
console.log(allResult);

🚀 まとめ

この Validation クラスを使用すれば、簡単にフォームバリデーションが実装できます。
また、カスタムバリデーションを追加することで、柔軟に対応可能です。
ぜひ、プロジェクトに取り入れてみてください!

1
0
1

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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?