👷♂️
ArkRegex: TypeScriptで型安全な正規表現を実現する
はじめに
正規表現のキャプチャグループ名をタイポしても、実行時まで気づけない...そんな経験はありませんか?
ArkRegexは、正規表現のキャプチャグループに対してTypeScriptの型推論を提供するライブラリです。これにより、エディタ上でのコード補完やコンパイル時の型チェックが可能になります。
インストール
npm install arkregex
基本的な使い方
import { regex } from "arkregex"
// パターンを定義
const pattern = regex("^(?<year>\\d{4})-(?<month>\\d{2})$")
// exec()でマッチング
const match = pattern.exec("2024-12")
if (match?.groups) {
console.log(match.groups.year) // "2024" - 補完が効く!
console.log(match.groups.month) // "12"
// タイポは型エラーになる
// match.groups.yesr // ❌ Property 'yesr' does not exist
// match.groups.day // ❌ Property 'day' does not exist
}
重要: exec() vs match() の違い
ArkRegexの型推論を活用するには、exec()を使用する必要があります。
exec() - 型推論あり
const pattern = regex("^(?<year>\\d{4})-(?<month>\\d{2})$")
const execResult = pattern.exec("2024-12")
if (execResult?.groups) {
const { year, month } = execResult.groups // 型安全な分割代入
// 以下は型エラーになる
// execResult.groups.yesr // ❌
// execResult.groups.day // ❌
}
match() - 従来通りのRegExpとして使用可能(型推論なし)
ArkRegexは従来のRegExpと同じようにStringのmatch()メソッドでの使用が可能です。
しかし戻り値が通常のRegExpMatchArrayとなり、ArkRegexの型情報は失われます。
const matchResult = "2024-12".match(pattern)
if (matchResult?.groups) {
// 従来のRegExpと同じように使用できるが、型推論は効かない
matchResult.groups.yesr // エラーにならない
matchResult.groups.day // エラーにならない
}
実践的なパターン例
日本の郵便番号
const zipCode = regex("^(?<zip1>\\d{3})-(?<zip2>\\d{4})$")
const match = zipCode.exec("150-0002")
if (match?.groups) {
console.log(`${match.groups.zip1}-${match.groups.zip2}`)
}
メールアドレス
const email = regex("^(?<username>[a-zA-Z0-9._-]+)@(?<domain>[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})$")
const match = email.exec("user@example.com")
if (match?.groups) {
console.log(`Username: ${match.groups.username}`)
console.log(`Domain: ${match.groups.domain}`)
}
URL
const url = regex("^(?<protocol>https?)://(?<host>[^/]+)(?<path>/.*)?$")
const match = url.exec("https://api.example.com/v1/users")
if (match?.groups) {
console.log(`Protocol: ${match.groups.protocol}`) // "https"
console.log(`Host: ${match.groups.host}`) // "api.example.com"
console.log(`Path: ${match.groups.path}`) // "/v1/users"
}
セマンティックバージョニング
const semver = regex("^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)$")
const match = semver.exec("2.1.25")
if (match?.groups) {
const { major, minor, patch } = match.groups
console.log(`v${major}.${minor}.${patch}`)
}
ArkTypeとの統合
ArkRegexはArkTypeのスキーマに組み込むことができます。
import { ArkErrors, type } from "arktype"
import { regex } from "arkregex"
const emailPattern = regex("^(?<username>[a-zA-Z0-9._-]+)@(?<domain>[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})$")
const userSchema = type({
name: "string",
email: emailPattern,
age: "number>=0"
})
const result = userSchema({
name: "太郎",
email: "taro@example.com",
age: 25
})
const error = userSchema({
name: "花子",
email: "invalid-email",
age: 5
})
console.log(result);
// { name: '太郎', email: 'taro@example.com', age: 25 }
console.log(error);
// ArkErrors(1)
// instanceof ArkErrorsでErrorかの判別が可能!
if (error instanceof ArkErrors) {
// summaryでエラーの内容の確認ができる
console.log(error.summary);
// email must be matched by ^(?<username>[a-zA-Z0-9._-]+)@(?<domain>[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$ (was "invalid-email")
}
パフォーマンス
ArkRegexはゼロランタイムオーバーヘッドを実現しています。
ベンチマーク条件: 100,000件 × 10回の平均
| ライブラリ | 平均 |
|---|---|
| ArkRegex | 6.41ms |
| RegExp | 6.12ms |
約5%の差(0.29ms)で、実用上は無視できるレベルです。
まとめ
-
ArkRegexは型安全な正規表現を提供
- キャプチャグループ名の補完
- タイポのコンパイル時検出
-
exec()を使用する
-
pattern.exec(string)の形で型推論が機能 -
string.match(pattern)では従来のRegExpとして使用可能(型推論なし)
-
-
ArkTypeとの統合
- スキーマバリデーションにArkRegexパターンを組み込み可能
- 正規表現とオブジェクト構造の両方を型安全にバリデーション
-
ゼロランタイムオーバーヘッド
- 型チェックはコンパイル時のみ
- 実行時パフォーマンスは通常のRegExpと同等
Discussion