まず「電話番号判定」でやりたいことをはっきりさせる
最初に決めておきたいのは、「どこまで厳しく判定するか」です。
電話番号って、現実にはいろんな形で入力されます。
03-1234-5678090-1234-567803 1234 567803−1234−5678(全角)+81-3-1234-5678
これを全部「ダメ」と言ってしまうと、ユーザーはかなりつらいです。
一方で、何でも通すと「電話番号じゃない文字列」も平気で入ってきます。
業務ユーティリティとしては、だいたい次の方針が現実的です。
「数字以外(ハイフン・スペースなど)は一旦無視して、“数字として妥当な長さかどうか”を見る」
これをベースにすると、実装もシンプルで、ユーザーにも優しい判定になります。
ステップ1:電話番号から「数字だけ」を取り出す
なぜ最初に数字だけにするのか
ハイフンやスペース、括弧などは、人間が読みやすくするための飾りです。
判定するときには邪魔なので、一旦全部取り除いて「数字だけ」にしてしまうのが楽です。
function extractDigits(value) {
if (value == null) return "";
return String(value).replace(/\D+/g, "");
}
JavaScriptここでのポイントを噛み砕きます。
String(value) で文字列に変換している(数値で渡されても対応できる)。\D は「数字以外の1文字」を意味する。\D+ は「数字以外が1文字以上続くところ」。replace(/\D+/g, "") で「数字以外を全部消す」。
動きはこんな感じです。
extractDigits("03-1234-5678"); // "0312345678"
extractDigits("090 1234 5678"); // "09012345678"
extractDigits("+81-3-1234-5678"); // "81312345678"
extractDigits("03−1234−5678"); // "03−1234−5678"(全角はまだ残る)
JavaScript全角数字も扱いたい場合は、後で「全角→半角」の正規化を足します。
ステップ2:全角数字・全角記号も半角にそろえる
日本語環境では「全角問題」を無視できない
日本語の入力だと、全角数字や全角ハイフンが普通に混ざります。
090−1234−5678
これをそのまま \D で処理しても、うまくいきません。
なので、「電話番号判定用の正規化」として、全角→半角を一度やっておくと安心です。
function toHalfWidth(str) {
if (str == null) return "";
return String(str)
.replace(/[!-~]/g, (ch) =>
String.fromCharCode(ch.charCodeAt(0) - 0xFEE0)
)
.replace(/ /g, " ");
}
JavaScriptこれで、全角英数字・記号・全角スペースを半角に寄せられます。
組み合わせるとこうなります。
function normalizePhoneRaw(value) {
const half = toHalfWidth(value);
return half.replace(/\D+/g, "");
}
JavaScript動きはこうです。
normalizePhoneRaw("090−1234−5678"); // "09012345678"
normalizePhoneRaw("03-1234-5678"); // "0312345678"
normalizePhoneRaw("+81-3-1234-5678"); // "81312345678"
JavaScriptここまで来ると、「電話番号として判定したい“数字列”」がきれいに取り出せます。
ステップ3:「数字列として妥当か」を判定する
どんなルールで「妥当」とみなすか
ここが設計の肝です。
日本の電話番号をざっくり見ると、だいたいこんな感じです。
固定電話:10桁(例: 0312345678)
携帯電話:11桁(例: 09012345678)
海外番号や内線などまで完璧にカバーしようとするとキリがないので、
まずは「10桁か11桁ならOK」というくらいから始めるのが現実的です。
function isPhoneNumber(value) {
const digits = normalizePhoneRaw(value);
if (digits.length < 10 || digits.length > 11) {
return false;
}
// 先頭が 0 で始まる日本の番号っぽいかどうか
if (!digits.startsWith("0")) {
return false;
}
return true;
}
JavaScriptポイントを噛み砕きます。
normalizePhoneRaw で「全角→半角+数字だけ」にしている。
長さが 10〜11 桁のものだけを許可している。
先頭が 0 で始まるものだけを許可している(日本の国内番号っぽさ)。
これだけでも、「明らかに電話番号じゃないもの」はかなり弾けます。
動きの確認
isPhoneNumber("03-1234-5678"); // true(10桁)
isPhoneNumber("090-1234-5678"); // true(11桁)
isPhoneNumber("090−1234−5678"); // true(全角でもOK)
isPhoneNumber("123-456"); // false(桁数不足)
isPhoneNumber("abc-090-1234-5678"); // true(数字だけ見ているのでOK)
isPhoneNumber("+81-3-1234-5678"); // false(813... で始まるので 0 始まりではない)
JavaScript「海外番号も許可したい」「内線も扱いたい」などの要件が出てきたら、
このルールを少しずつ広げていくイメージです。
応用:携帯だけ/固定電話だけを判定したい場合
携帯番号っぽいかどうか
日本の携帯番号は、だいたい 090, 080, 070 などで始まる11桁です。
ざっくり判定するなら、こう書けます。
function isMobilePhone(value) {
const digits = normalizePhoneRaw(value);
if (digits.length !== 11) return false;
return (
digits.startsWith("090") ||
digits.startsWith("080") ||
digits.startsWith("070")
);
}
JavaScript固定電話っぽいかどうか
固定電話は 10 桁で、0 から始まります。
細かい市外局番ルールまでやると大変なので、まずは長さだけ見ます。
function isFixedLinePhone(value) {
const digits = normalizePhoneRaw(value);
if (digits.length !== 10) return false;
return digits.startsWith("0");
}
JavaScriptここまで分けておくと、
「携帯だけ入力してほしいフォーム」
「固定電話だけ入力してほしいフォーム」
などで、ユーティリティを使い分けられます。
業務での具体的な使いどころ
フロントエンドの入力チェック
フォームで電話番号を入力してもらうとき、
「明らかにおかしいもの」を早めに教えてあげる用途です。
const phone = phoneInput.value;
if (!isPhoneNumber(phone)) {
showError("電話番号の形式が正しくありません。数字とハイフンで入力してください。");
}
JavaScriptここで大事なのは、
「形式がOKだからといって、必ず実在する番号とは限らない」
「形式が少し変でも、実際には有効な番号の可能性もある」
ということを理解したうえで、
「入力ミスを減らすためのガイド」として使うことです。
バックエンド側の軽いバリデーション
API で電話番号を受け取るときにも、
最低限の形式チェックをしておくと、
明らかにおかしいデータが DB に入るのを防げます。
function validatePhoneOrThrow(phone) {
if (!isPhoneNumber(phone)) {
throw new Error("Invalid phone number format");
}
}
JavaScriptただし、「実在する番号かどうか」は別問題なので、
必要なら別のプロセス(SMS 認証など)で確認します。
設計として意識してほしいこと
「正規化」と「判定」を分ける
ここが一番大事なポイントです。
全角→半角
数字だけを取り出す
といった「正規化」と、
桁数チェック
先頭の数字チェック
といった「判定」は、役割が違う処理です。
これを一つの関数にごちゃっと詰め込むと、
後から読む人が「何をどこまでやっているのか」分かりづらくなります。
なので、
export function toHalfWidth(str) { ... }
export function normalizePhoneRaw(value) { ... } // 全角→半角+数字だけ
export function isPhoneNumber(value) { ... } // 桁数+先頭チェック
export function isMobilePhone(value) { ... }
export function isFixedLinePhone(value) { ... }
JavaScriptのように、段階ごとに関数を分けておくと、
テストもしやすいし、仕様変更にも強くなります。
「どこまで厳しくするか」をチームで決める
電話番号判定は、プロダクトごとに「ちょうどいい厳しさ」が違います。
海外番号も扱うのか。
内線や内線番号付き(03-1234-5678 ext.123)も許可するのか。
携帯だけ/固定だけを許可したいのか。
これを曖昧にしたまま実装を始めると、
画面ごとにバラバラなルールになってしまいます。
なので、
まずは「数字だけにして 10〜11 桁ならOK」というベースを作る。
必要に応じて isMobilePhone や isFixedLinePhone を足していく。
というステップで育てていくのがおすすめです。
ちょっとだけ手を動かしてみる
コンソールで、次のあたりを試してみてください。
normalizePhoneRaw("090−1234−5678");
normalizePhoneRaw("03-1234-5678");
isPhoneNumber("03-1234-5678");
isPhoneNumber("123-456");
isMobilePhone("090-1234-5678");
isFixedLinePhone("03-1234-5678");
JavaScript「どこからが true で、どこからが false になるか」を体で感じてみてください。
そのうえで、自分のプロジェクトに
export function normalizePhoneRaw(value) { ... }
export function isPhoneNumber(value) { ... }
JavaScriptを一つ置いて、
「電話番号っぽいかどうかを見たいときは、必ずここを通す」
というルールにしてみてください。
それができた瞬間、あなたのバリデーションは
「その場しのぎの if と正規表現」から
「意図を持って設計された検証ユーティリティ」に、一段レベルアップします。
