JavaScript Tips | 文字列ユーティリティ:検証 - 電話番号判定

JavaScript JavaScript
スポンサーリンク

まず「電話番号判定」でやりたいことをはっきりさせる

最初に決めておきたいのは、「どこまで厳しく判定するか」です。

電話番号って、現実にはいろんな形で入力されます。

03-1234-5678
090-1234-5678
03 1234 5678
03−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」というベースを作る。
必要に応じて isMobilePhoneisFixedLinePhone を足していく。

というステップで育てていくのがおすすめです。


ちょっとだけ手を動かしてみる

コンソールで、次のあたりを試してみてください。

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 と正規表現」から
「意図を持って設計された検証ユーティリティ」に、一段レベルアップします。

タイトルとURLをコピーしました