TypeScript での文字列検索・操作チートシート — 用途別コード集

TypeScript での文字列検索と操作のための実践的なコード集です。よくある用途別に整理しているため、必要な実装をすぐに見つけられます。

基本的な文字検索

単一の文字・部分文字列を検索

// includes() — 最も推奨される方法
function containsCharacter(text: string, char: string): boolean {
  return text.includes(char);
}

// indexOf() — 位置も取得したい場合
function hasCharacter(text: string, char: string): boolean {
  return text.indexOf(char) !== -1;
}

const text = "Hello, TypeScript!";
console.log(containsCharacter(text, "Type")); // true
console.log(text.indexOf("Type"));            // 7

大文字・小文字を区別しない検索

function containsIgnoreCase(text: string, search: string): boolean {
  return text.toLowerCase().includes(search.toLowerCase());
}

console.log(containsIgnoreCase("Hello TypeScript", "typescript")); // true

先頭・末尾の確認

const url = "https://example.com/api/users";

url.startsWith("https://");  // true
url.endsWith("/users");       // true

// 複数候補のいずれかで始まるか
const schemes = ["https://", "http://"];
schemes.some(s => url.startsWith(s)); // true

パターンマッチング(正規表現)

// 基本パターン検索
function matchesPattern(text: string, pattern: string): boolean {
  return new RegExp(pattern).test(text);
}

// よく使う判定
const hasDigit = (text: string): boolean => /\d/.test(text);
const hasLetter = (text: string): boolean => /[a-zA-Z]/.test(text);
const hasSpecialChar = (text: string): boolean => /[!@#$%^&*()]/.test(text);
const isEmail = (text: string): boolean => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(text);
const isURL = (text: string): boolean => /^https?:\/\/.+/.test(text);

// 特殊文字を含む文字列の安全な検索(エスケープ処理)
function findSpecialChar(text: string, specialChar: string): boolean {
  const escapedChar = specialChar.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
  return new RegExp(escapedChar).test(text);
}

複数文字の検索

// いずれかの文字列を含むか
function containsAny(text: string, searchTerms: string[]): boolean {
  return searchTerms.some(term => text.includes(term));
}

// すべての文字列を含むか
function containsAll(text: string, searchTerms: string[]): boolean {
  return searchTerms.every(term => text.includes(term));
}

console.log(containsAny("Hello", ["x", "H", "z"]));          // true
console.log(containsAll("Hello World", ["Hello", "World"])); // true

入力バリデーション

パスワード強度チェック

type ValidationResult = { valid: boolean; messages: string[] };

function validatePassword(password: string): ValidationResult {
  const checks = [
    { test: (p: string) => p.length >= 8,     message: "8文字以上必要です" },
    { test: (p: string) => /[A-Z]/.test(p),   message: "大文字が必要です" },
    { test: (p: string) => /[a-z]/.test(p),   message: "小文字が必要です" },
    { test: (p: string) => /[0-9]/.test(p),   message: "数字が必要です" },
    { test: (p: string) => /[!@#$%^&*]/.test(p), message: "特殊文字が必要です" },
  ];

  const failedChecks = checks
    .filter(check => !check.test(password))
    .map(check => check.message);

  return { valid: failedChecks.length === 0, messages: failedChecks };
}

// 使用例
const result = validatePassword("Pass123!");
console.log(result.valid);    // true
console.log(result.messages); // []

XSS 対策の入力サニタイズ

function sanitizeInput(input: string): string {
  return input
    .replace(/&/g, "&")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#039;");
}

// 使用例
const userInput = "<script>alert('XSS')</script>";
console.log(sanitizeInput(userInput));
// &lt;script&gt;alert(&#039;XSS&#039;)&lt;/script&gt;

テキスト分析

文字の出現回数をカウント

// indexOf を使った実装
function countOccurrences(text: string, target: string): number {
  let count = 0;
  let pos = text.indexOf(target);
  while (pos !== -1) {
    count++;
    pos = text.indexOf(target, pos + 1);
  }
  return count;
}

// 簡潔な代替実装
function countOccurrencesAlt(text: string, target: string): number {
  return text.split(target).length - 1;
}

console.log(countOccurrences("banana", "an")); // 2

文字の全位置を取得

function findAllPositions(text: string, target: string): number[] {
  const positions: number[] = [];
  let pos = text.indexOf(target);
  while (pos !== -1) {
    positions.push(pos);
    pos = text.indexOf(target, pos + 1);
  }
  return positions;
}

console.log(findAllPositions("abcabc", "a")); // [0, 3]

文字列の出現頻度マップ

function charFrequency(text: string): Map<string, number> {
  return [...text].reduce((map, char) => {
    map.set(char, (map.get(char) ?? 0) + 1);
    return map;
  }, new Map<string, number>());
}

パフォーマンス最適化

早期リターン

function efficientSearch(text: string, terms: string[]): boolean {
  for (const term of terms) {
    if (text.includes(term)) {
      return true; // 一致が見つかった時点で終了
    }
  }
  return false;
}

検索結果のキャッシング

const searchCache = new Map<string, boolean>();

function cachedSearch(text: string, search: string): boolean {
  const cacheKey = `${text}:${search}`;
  if (searchCache.has(cacheKey)) {
    return searchCache.get(cacheKey)!;
  }
  const result = text.includes(search);
  searchCache.set(cacheKey, result);
  return result;
}

大きなテキストの処理

function searchLargeText(text: string, search: string): boolean {
  const chunkSize = 10000;
  for (let i = 0; i < text.length; i += chunkSize) {
    // search がチャンクをまたがる可能性を考慮してオーバーラップさせる
    const chunk = text.slice(i, i + chunkSize + search.length - 1);
    if (chunk.includes(search)) return true;
  }
  return false;
}

特殊ケースの処理

Unicode 文字・絵文字の正確なカウント

// スプレッド構文でサロゲートペアを正しく扱う
function countUnicodeChars(text: string): number {
  return [...text].length;
}

// 絵文字を含む文字列の確認
function containsEmoji(text: string): boolean {
  const emojiRegex = /[\u{1F300}-\u{1F6FF}\u{1F900}-\u{1F9FF}]/u;
  return emojiRegex.test(text);
}

console.log("👋".length);              // 2(UTF-16 コードユニット)
console.log(countUnicodeChars("👋")); // 1(正しい文字数)

文字列置換

// 全ての出現を置換(ES2021 以降)
const modernReplaceAll = (text: string, search: string, replace: string): string =>
  text.replaceAll(search, replace);

// 後方互換性が必要な場合
const legacyReplaceAll = (text: string, search: string, replace: string): string =>
  text.split(search).join(replace);

実践的なツール関数

URL からクエリパラメータを抽出

function getQueryParam(url: string, param: string): string | null {
  return new URL(url).searchParams.get(param);
}

console.log(getQueryParam("https://example.com/?id=123&name=test", "id")); // "123"

スラッグ生成(ブログ記事 URL 等)

function toSlug(text: string): string {
  return text
    .toLowerCase()
    .replace(/[^\w\s-]/g, "")   // 英数字・スペース・ハイフン以外を除去
    .replace(/\s+/g, "-")        // スペースをハイフンに
    .replace(/-+/g, "-")         // 連続ハイフンを単一に
    .replace(/^-|-$/g, "");      // 先頭・末尾のハイフンを除去
}

console.log(toSlug("Hello World! This is TypeScript")); // "hello-world-this-is-typescript"

まとめ

  • 単純な検索: includes() が最も読みやすく推奨
  • 位置が必要: indexOf() / lastIndexOf()
  • パターンマッチ: 正規表現(test() / match()
  • Unicode・絵文字: スプレッド構文 [...text] でサロゲートペアを正しく扱う
  • パフォーマンス: 大量データには早期リターン・キャッシング・チャンク処理を組み合わせる