Groups and alternation are the structural backbone of any non-trivial regex. In TS-Rex, these methods do more than emit regex syntax — they directly shape the TypeScript type of the compiled result. EveryDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/fajarnugraha37/ts-rex/llms.txt
Use this file to discover all available pages before exploring further.
.capture() call widens TCaptures, every .or() converts both sides to Partial, and .matchPrevious() enforces at compile time that you only reference a name you’ve already captured. Understanding the type-level semantics of these four methods is the key to getting precise inference from .exec().
.group(builder)
Wraps the inner pattern in a non-capturing group ((?:...)). Captures defined inside the inner builder are merged into the outer TCaptures, so they remain accessible on the final result — the group itself does not introduce a new name.
Signature
A builder whose pattern will be wrapped in
(?:...). Any captures defined on this builder are intersected into the outer type..group() call is (?:(?<scheme>https?)). The scheme capture is fully available on the result even though it was defined inside a non-capturing group.
.capture(name, builder)
Wraps the inner pattern in a named capturing group ((?<Name>...)), adding Record<Name, string> to TCaptures. The name argument must be a valid JavaScript identifier; the runtime throws if it isn’t.
Signature
The capture group name. Must match
/^[a-zA-Z_][a-zA-Z0-9_]*$/ — a valid JavaScript identifier. Throws Error at runtime if invalid.The pattern to capture. Any captures nested inside this builder are also merged into
TCaptures.Name validation is enforced at runtime by a regex test:
/^[a-zA-Z_][a-zA-Z0-9_]*$/. Names like "2fast" or "my-group" will throw. TypeScript does not prevent invalid names at compile time because Name extends string is unconstrained — validation is a runtime guard..capture() call intersects Record<Name, string> into TCaptures. After two captures, the type is:
.exec() will have result.firstName: string and result.lastName: string when isMatch is true.
Example
builder argument are merged into the outer type alongside the outer capture name:
.or(builder)
Matches either the pattern accumulated so far or the pattern in the passed builder, emitting (?:left|right). At the type level, both TCaptures and the inner builder’s captures are converted to Partial, reflecting the fact that only one branch fires per match.
Signature
The alternative branch. Its captures are merged as
Partial into the result type.Union semantics:
.or() does not produce a TypeScript union (A | B). It produces an intersection of partials (Partial<A> & Partial<B>). This means every capture from both branches appears on the result type, but all are string | undefined. Use if (result.a !== undefined) guards to determine which branch matched. This is an intentional trade-off: discriminated unions on named captures are not expressible without knowing the branch at compile time..or() is fully chainable, building up (?:(?:a|b)|c) style nesting:
.matchPrevious(name)
Emits a backreference (\k<name>) that matches exactly the same text captured by a previously named group. The name argument is constrained to keyof TCaptures, so TypeScript raises a type error if you reference a group that hasn’t been captured yet in the chain.
Signature
The name of a previously defined capture group. TypeScript enforces this statically — passing an unknown name is a compile-time error.