All files / src/utils string.ts

100% Statements 40/40
91.66% Branches 22/24
100% Functions 10/10
100% Lines 40/40

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114        1x 3x           1x 3x           1x 1x           1x 2x             1x 1x             1x 1x             1x 1x           1x 1x 1x 1x           1x 1x 1x 1x                                           1x 1x 1x         1x 1x 1x 1x 1x 1x   1x 1x 1x 4x 4x 2x 1x 1x 1x  
/**
 * camelCase → snake_case
 * @param str
 */
export const toSnake = (str: string): string =>
  str.replace(/[A-Z]/g, (m) => `_${m.toLowerCase()}`)
 
/**
 * snake_case → camelCase
 * @param str
 */
export const toCamel = (str: string): string =>
  str.replace(/_([a-z])/g, (_, c) => c.toUpperCase())
 
/**
 * 全角・半角スペース含めて左右をトリム
 * @param str
 */
export const trimBoth = (str: string): string =>
  str.replace(/^[\s ]+|[\s ]+$/g, '')
 
/**
 * null/undefined/空文字の判定
 * @param str
 */
export const isEmpty = (str: unknown): boolean =>
  str === null || str === undefined || String(str).trim() === ''
 
/**
 * 文字列の先頭チェック(CSRF トークン・URL・prefix などで使用)
 * @param str
 * @param prefix
 */
export const startsWith = (str: string, prefix: string): boolean =>
  str.startsWith(prefix)
 
/**
 * 文字列の末尾チェック(CSRF トークン・URL・prefix などで使用)
 * @param str
 * @param suffix
 */
export const endsWith = (str: string, suffix: string): boolean =>
  str.endsWith(suffix)
 
/**
 * 文字列の短縮(ellipsis)
 * @param str
 * @param max
 */
export const truncate = (str: string, max: number): string =>
  str.length <= max ? str : `${str.slice(0, max)}…`
 
/**
 * URL / path の join(余計な / を防ぐ)
 * @param parts
 */
export const joinPath = (...parts: string[]): string =>
  parts
    .map((p) => p.replace(/^\/+|\/+$/g, '')) // 両端の / を除去
    .join('/')
 
/**
 * 日本語の全角 → 半角変換(ユーザー入力で必要になることがある)
 * @param str
 */
export const toHalfWidth = (str: string): string =>
  str
    .replace(/[!-~]/g, (ch) => String.fromCharCode(ch.charCodeAt(0) - 0xfee0))
    .replace(/ /g, ' ')
 
/**
 * 安全に文字列配列を結合するユーティリティ。
 * null / undefined / 空文字を除外し、必要に応じて trim も行う。
 *
 * @param parts - 結合対象の文字列。null / undefined を含んでもよい。
 * @param options - 動作カスタマイズのオプション
 * @param options.separator - 結合時に使用する区切り文字(デフォルト: '')
 * @param options.trim - 前後の空白を削除するか(デフォルト: true)
 * @param options.omitEmpty - 空文字('')を除外するか(デフォルト: true)
 *
 * @returns 有効な文字列だけを結合した結果の文字列
 *
 * @example
 * joinNonEmpty(['東京', '中野区', null, '江古田'])
 * // => "東京中野区江古田"
 *
 * @example
 * joinNonEmpty(['A', ' B ', ''], { separator: ', ', trim: true })
 * // => "A, B"
 */
export const joinNonEmpty = (
  parts: Array<string | null | undefined>,
  options?: {
    separator?: string
    trim?: boolean
    omitEmpty?: boolean
  }
): string => {
  const {
    separator = '',
    trim = true,
    omitEmpty = true,
  } = options ?? {}
 
  return parts
    .map((v) => (trim && typeof v === 'string' ? v.trim() : v))
    .filter((v): v is string => {
      if (v == null) return false
      if (omitEmpty && v === '') return false
      return typeof v === 'string'
    })
    .join(separator)
}