概要
バリデーションとは「値のチェック」ではなく、
「正しさの定義」と「設計的責任」の明示である。
UIに紐づいたアドホックなバリデーションは、
保守不能なフォーム構造を生む。
本稿では、バリデーションロジックの抽象化、入力UIとの分離、エラーメッセージの戦略的設計、同期/非同期チェックの構造化、再利用のための分離原則を軸に、信頼できるフォーム構造の構築法を提示する。
1. バリデーションを「責務」で分離する
function validateEmail(email) {
if (!email) return '必須項目です';
if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) return 'メール形式が不正です';
return null;
}
- ✅ 入力のチェックロジックはUIとは切り離して構築
- ✅ 戻り値は
"エラーメッセージ"
ornull
で統一 → 分岐コストを減らす
2. フィールド単位でルールを宣言する
const validators = {
email: [isRequired, isEmail],
password: [isRequired, minLength(8)],
};
function runValidations(value, rules) {
for (const rule of rules) {
const error = rule(value);
if (error) return error;
}
return null;
}
- ✅ 複数ルールをループ評価
- ✅ 入力フィールドにバリデーションルールを“宣言”できる構造に
3. UIとの接着部分は別レイヤーで制御
const emailInput = document.querySelector('#email');
const errorLabel = document.querySelector('#email-error');
emailInput.addEventListener('blur', () => {
const msg = runValidations(emailInput.value, validators.email);
errorLabel.textContent = msg || '';
});
→ ✅ バリデーションロジックとDOM操作のレイヤーを明確に分離
4. 非同期バリデーションの組み込み
async function checkEmailDuplication(email) {
const res = await fetch(`/api/email-check?email=${email}`);
const isTaken = await res.json();
return isTaken ? '既に登録されています' : null;
}
async function validateFieldAsync(value, rules) {
for (const rule of rules) {
const result = await rule(value);
if (result) return result;
}
return null;
}
- ✅ 同期と非同期のルールを分離設計 →
validateSync()
/validateAsync()
両立設計へ
5. 全体バリデーションの設計構造
async function validateForm(values, validators) {
const errors = {};
for (const key in validators) {
const rules = validators[key];
const value = values[key];
const error = await validateFieldAsync(value, rules);
if (error) errors[key] = error;
}
return errors;
}
→ ✅ “全体として正しいか”をチェックするAPI用の検証構造を統合設計
6. メッセージの国際化・汎用化構造
const messages = {
required: '必須項目です',
invalidEmail: 'メール形式が不正です',
};
function isRequired(value) {
return !value ? messages.required : null;
}
- ✅ ロジックと文言を分離
- ✅ i18nとの統合が容易な構造に
設計判断フロー
① ロジックがUI内に埋もれていないか? → 関数分離
② 同期/非同期チェックが混在してないか? → 分離して統合設計
③ ルールが再利用可能になっているか? → フィールド単位の宣言型に
④ 文言がロジックにハードコードされていないか? → メッセージ分離
⑤ DOMとの接着部分がテスト不能になっていないか? → UIとロジック分離
よくあるミスと対策
❌ UIイベント内で直接 if (!value) { showError(...) }
を繰り返す
→ ✅ ルールごとの関数を定義し、宣言的に登録する構造へ
❌ 非同期チェックを onSubmit
に直書きして処理が複雑化
→ ✅ 非同期ルールを同期ルールと同様の形式で処理できる構造に
❌ エラー文言がHTMLにハードコードされていて再利用不能
→ ✅ メッセージは定義モジュールから取得する形式に統一
結語
フォームバリデーションとは、“正しいか”を判定する行為ではない。
それは**“何を許可し、どこで止め、どう伝えるか”という、設計上の責任の表明**である。
- UIとロジックを分離し
- 責務を関数に還元し
- メッセージを柔軟に制御する
JavaScriptにおけるバリデーション設計とは、
“意図された操作だけを許容する、安全なインターフェースの設計である”。