TSで正規表現を型安全に扱う「ArkRegex」を試してみた
はじめに
最近 X で話題になっていた ArkRegex というものを触ってみたので紹介します!
ArkRegex とは
ArkRegex は、公式によるとnew RegExp()の型安全なドロップインリプレースであると明記されています。
普段new RegExp()を使っているところを ArkRegex に置き換えると:
- 正規表現パターンから文字列の型が推論される
- 構文エラーが型エラーとして検出される
-
.test()で入力文字列の妥当性チェックができる -
.exec()のキャプチャグループが型安全になる
インストール
pnpm install arkregex
使ってみる
例として、ユーザー ID のバリデーション(英小文字+数字、3〜12 文字)を作ってみる。
import { regex } from "arkregex";
const userId = regex("^[a-z0-9]{3,12}$");
console.log(userId.test("abc123")); // true
console.log(userId.test("ABC")); // false(大文字はNG)
使い方自体は普通のRegExpと全く同じですが、IDE でマウスホバーしてみると型情報が付いています。

通常のRegExpはRegExp型ですが、ArkRegex はRegex<string, {}>という型になり、より詳細な型情報を持ちます。
型推論がすごい
大文字小文字の組み合わせを全部推論
例えば、iフラグ(大文字小文字を区別しない)を使った場合、こんなことが起きます。
const ok = regex("^ok$", "i");
IDE でこのokの型を見ると:
Regex<"ok" | "oK" | "Ok" | "OK", { flags: "i" }>;
おぉ、大文字小文字の全組み合わせがユニオン型として推論されてる。
オプショナルなパターンも
電話番号のパターンで、ハイフンがあってもなくても OK にしたい場合:
const phone = regex("^0\\d{1,4}-?\\d{1,4}-?\\d{4}$");
この型を見ると:
Regex<
| `0${bigint}${bigint}${bigint}` // ハイフンなし
| `0${bigint}-${bigint}${bigint}` // 1つ目だけ
| `0${bigint}${bigint}-${bigint}` // 2つ目だけ
| `0${bigint}-${bigint}-${bigint}`, // 両方あり
{}
>;
ハイフンが 2 箇所で-?になっているから、2² = 4 パターン全部が型に表現されています。
もちろん実際に動かしても期待通りになります。
console.log(phone.test("090-1234-5678")); // true
console.log(phone.test("09012345678")); // true
console.log(phone.test("090-12345678")); // true
キャプチャグループが型安全に
位置指定キャプチャグループ
セマンティックバージョニングのパターンを書いてみる:
const semver = regex("^(\\d+)\\.(\\d+)\\.(\\d+)$");
const match = semver.exec("2.1.25");
if (match) {
console.log(match[0]); // "2.1.25"
console.log(match[1]); // "2"
console.log(match[2]); // "1"
console.log(match[3]); // "25"
}
これだけなら普通なんですが、match[0]の型を見ると`${bigint}.${bigint}.${bigint}`になっています。
つまり、数字.数字.数字という形式だと型レベルで分かるわけですね。
名前付きキャプチャグループ
さらに便利なのが名前付きキャプチャグループで、メールアドレスをパースしてみる:
const email = regex("^(?<name>\\w+)@(?<domain>\\w+\\.\\w+)$");
const match = email.exec("user@example.com");
if (match && match.groups) {
console.log(match.groups.name); // "user"
console.log(match.groups.domain); // "example.com"
}
match.groups.と打つと、IDE でnameやdomainの補完が効きます。
しかも、タイプミスすると型エラーになります。
// これは型エラーになる
// console.log(match.groups.namee); // ❌ Property 'namee' does not exist
これで typo による実行時エラーを防げます。
実際に使えそうなパターン
いくつか実用的なパターンを試してみました:
郵便番号
const postalCode = regex("^(\\d{3})-(\\d{4})$");
const match = postalCode.exec("123-4567");
if (match) {
console.log(match[1]); // "123"
console.log(match[2]); // "4567"
}
URL
const url = regex(
"^(?<protocol>https?)://(?<host>[\\w.-]+)(?<path>/[\\w/.-]*)?$"
);
const match = url.exec("https://arktype.io/docs/blog/arkregex");
if (match && match.groups) {
console.log(match.groups.protocol); // "https"
console.log(match.groups.host); // "arktype.io"
console.log(match.groups.path); // "/docs/blog/arkregex"
}
match.groupsの型を見ると、オプショナルなpathグループの有無によって 2 つのパターンに分かれています:
{
protocol: "http" | "https";
host: string;
path: `/${string}`;
} | {
protocol: "http" | "https";
host: string;
path: undefined;
}
このように、オプショナルなキャプチャグループもちゃんとundefinedの可能性を含んだ型として推論されます。
ハッシュタグの抽出
gフラグを使った例も試してみる:
const hashtag = regex("#(\\w+)", "g");
const text =
"I love TypeScript! #typescript #arktype #arkregex and type safety!";
const tags = text.match(hashtag);
console.log(tags);
// ["#typescript", "#arktype", "#arkregex"]
ちゃんと全部拾えていますね 👍
日付
const datePattern = regex("^(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})$");
const match = datePattern.exec("2025-10-30");
if (match && match.groups) {
console.log(match.groups.year); // "2025"
console.log(match.groups.month); // "10"
console.log(match.groups.day); // "30"
}
まとめ
個人的には、名前付きキャプチャグループの補完が効くのが一番嬉しいですね。
今まで"これ何だっけ?"ってなってソースを見に行くことが多かったので。
正規表現を多用するプロジェクトで、型安全性を高めたい方にはおすすめできると思います。
気になった方はぜひ試してみてください!
初めての技術記事なので、内容に間違いや補足がありましたら、コメントで教えていただけると嬉しいです 🙏
Discussion