RegExp
PCRE2 under the hood, but aligned to the ECMAScript dialect by default so the everyday edge cases (`$` at the true end, an unset backreference matching empty, `\uXXXX` escapes) behave as real JS does. The recurring divergences are structural: strings are byte-indexed, so match offsets are byte positions unless you opt into `-regex=es-utf16`; `exec` results carry `index`, `input` and `groups`, and so does each step of `matchAll`'s iterator. The whole-program `-regex=<mode>` flag trades fidelity for speed across five modes, and the `u`/`d` flags are out of scope for V1 (`y`/sticky is implemented).
Construction: new RegExp(pattern, flags?)
supportedCompiles the pattern once via PCRE2 and throws a real, catchable SyntaxError on an invalid pattern. The flags string is validated as in real JS — a duplicate flag ("gg") or an unrecognized flag character ("z") throws a SyntaxError. A \uXXXX/\xXX escape compiles as an ECMAScript escape in the default and es-ascii modes.
Parameters- patternstring
- The pattern source.
- flagsstring
- Optional flag string (e.g. "gi").
RegExp — A compiled RegExp.
const fromCtor = new RegExp("[a-z]+@[a-z]+\\.[a-z]+", "i")
console.log(fromCtor.source)Literal syntax: /pattern/flags
Works, with caveatsRegex literals desugar to the same construction at parse time. An unescaped '/' inside a [...] character class does not end the literal.
const fromLiteral = /[a-z]+@[a-z]+\.[a-z]+/i
const withSlashClass = /[a/b]/
console.log(withSlashClass.source) // [a/b]x in /foo/mis-lexes the/as division (the lexer's regex-vs-division disambiguation gap, sinceinisn't its own token in this lexer) — a small, deliberately-accepted gap
.source / .flags
ExactField reads decomposed once at construction: .flags reports the canonical d,g,i,m,s,u,v,y order regardless of how the flags were given, and an empty pattern's .source is the spec placeholder "(?:)".
Returnsstring — The pattern source / flag string.
const r = new RegExp("^\\d+$", "gm")
console.log(r.flags) // gm.global / .ignoreCase / .multiline / .dotAll
ExactThe four boolean flag accessors, decomposed from the flags string once at construction — no method needs to re-parse it.
Returnsboolean — Whether the corresponding flag is set.
const r = new RegExp("^\\d+$", "gm")
console.log(r.global) // 1
console.log(r.multiline) // 1.lastIndex
ExactThe iteration cursor: mutated by .exec()/.test()/global str.match()/str.matchAll()/str.replace()/str.replaceAll() (advances on a successful g-flagged match, resets to 0 after a failed one), and deliberately untouched by str.split()/str.search(), matching real JS.
Returnsnumber — The next match start offset.
const g = /\d+/g
let match = g.exec("a1 b22 c333")
while (match !== null) { console.log(match[0]); match = g.exec("a1 b22 c333") }
console.log(g.lastIndex) // 0.test(str)
ExactA real PCRE2 match returning a boolean. A g-flagged RegExp reads/advances .lastIndex exactly as .exec() does, so a while (re.test(s)) loop steps through every match; a non-global RegExp always matches from offset 0 and never touches .lastIndex.
Parameters- strstring
- The subject string.
boolean — Whether the pattern matches.
const digits = /[0-9]+/
console.log(digits.test("abc123")) // 1
console.log(digits.test("abcdef")) // 0.exec(str)
ExactReturns string[] | null — index 0 is the full match, 1..N are the numbered capture groups; real, working m !== null / if (m) checks against the result. A global RegExp reads/writes .lastIndex exactly like real JS, so the while ((m = re.exec(s)) !== null) idiom works. All matching methods route through the one compiled PCRE2 handle, so the -regex dialect mode applies uniformly here and to match/matchAll/replace/replaceAll/split/search.
Parameters- strstring
- The subject string.
string[] | null — Match array, or null on no match.
const range = /(\d+)-(\d+)/
const m = range.exec("range: 12-34 end")
if (m !== null) { console.log(m[1]); console.log(m[2]) } // 12 / 34str.match(regexp)
Works, with caveatsWithout g: identical to regexp.exec(str). With g: full-match-only strings, and null (not []) on zero matches — matching real JS exactly.
Parameters- regexpRegExp
- The pattern to match.
string[] | null — Match array, or null.
const allDigits = "a1 b22 c333".match(/\d+/g)
if (allDigits !== null) { console.log(allDigits.length) } // 3
console.log("nothing".match(/\d+/g) === null) // true- With a regex held in a variable (not a literal), the result has no
index/input/groups; a non-global literal's result carries them, as.exec()'s does
str.matchAll(regexp)
ExactA RegExp String Iterator over every match, each step an exec result with index, input and groups. It runs on a clone of the RegExp, starting at its lastIndex, so the original's lastIndex is untouched. Throws a TypeError without the g flag.
Parameters- regexpRegExp
- A g-flagged pattern.
RegExpStringIterator<RegExpExecArray> — An iterator of exec results.
for (const each of "a-1 b-22 c-333".matchAll(/(\w)-(\d+)/g)) {
console.log(each[0], each.index)
}str.replace(regexp, replacement)
Works, with caveatsReplaces the first match without g, every match with g; the callback runs exactly once per match.
Parameters- regexpRegExp
- The pattern to match.
- replacementstring | function
- Template string or a callback.
string — The resulting string.
console.log("range: 12-34 end".replace(/(\d+)-(\d+)/, "[$2:$1]")) // range: [34:12] end
console.log("a1 b22 c333".replace(/\d+/g, (m: string, offset: number) => m + "@" + offset))- Replacement template supports
$1-$9/$&/$$only (`$`/$'` — pre-/post-match text — are out of scope) - The callback form is invoked with a fixed
(match, offset, string)— real JS's variadic...capturedGroupsin the middle isn't supported (a callback's arity is fixed at compile time but a pattern's capture count is only known at runtime; a callback declaring more than 3 parameters is a compile-time error)
str.replaceAll(regexp, replacement)
Works, with caveatsSame replacement rules as .replace(); requires the g flag, throwing TypeError otherwise.
Parameters- regexpRegExp
- A g-flagged pattern.
- replacementstring | function
- Template string or a callback.
string — The resulting string.
console.log("cat dog cat".replaceAll(/cat/g, "DOG")) // DOG dog DOG- Same replacement narrowing as
.replace()($1-$9/$&/$$only; fixed(match, offset, string)callback)
str.split(regexp)
ExactFinds every match regardless of the g flag (its own local search loop, like real JS), splicing the separator's capture groups in after each piece (an unmatched one as undefined). A regex that never matches returns a single-element array holding the whole subject.
Parameters- regexpRegExp
- The delimiter pattern.
string[] — The split pieces.
const parts = "a1b22c333d".split(/\d+/)
console.log(parts.length) // 4str.search(regexp)
supportedAlways searches from offset 0, restores .lastIndex afterward — invisible to later .exec()/.test() iteration, matching real JS exactly. A plain-string argument is coerced to a RegExp as in real JS (metacharacters interpreted). Returns -1 on no match.
Parameters- regexpRegExp
- The pattern to search for.
number — Match offset, or -1.
console.log("hello world".search(/world/)) // 6
console.log("hello world".search(/xyz/)) // -1y (sticky) flag
ExactA sticky RegExp matches only at exactly .lastIndex: the match is anchored there, .lastIndex advances to the match end on success and resets to 0 on failure, exactly as for g. The real RegExpBuiltinExec rule — lastIndex is honoured when global OR sticky is set — flows through exec, test, match, matchAll, replace, replaceAll and search.
Returnsboolean — Whether the y flag is set.
const re = /foo/y
re.lastIndex = 3
console.log(re.test("barfoo"), re.lastIndex) // true 6
console.log(re.test("barfoo"), re.lastIndex) // false 0u / d flags
Not supportedThe u (unicode) and d (indices) flags are deferred for the whole feature's V1. \u{…} code-point escapes, surrogate handling, Unicode \p{…}/class semantics, and strict-syntax validation are a later option.