fix(type): preserve escaped backslash in regex string literals#1634
Open
spokodev wants to merge 1 commit into
Open
fix(type): preserve escaped backslash in regex string literals#1634spokodev wants to merge 1 commit into
spokodev wants to merge 1 commit into
Conversation
A regex written as a string literal was parsed with the string-escape
rule, which collapses `\\` into a single `\`. That is correct for a JS
string but corrupts a regex source, where `\\` is a literal backslash
and `\d`/`\b`/`\w` are metacharacters. So `type("/^\\\\d$/")` silently
became the digit-class regex `/^\d$/`, accepting "5" and rejecting "\d",
disagreeing with the equivalent RegExp-instance form.
Add an optional `escapeEscape` argument to `shiftUntilEscapable` and pass
a backslash for regex tokens in both the runtime and type-level parsers,
so a doubled backslash is preserved in the pattern source. Quoted-string
and date literals keep collapsing `\\` as before; escaped terminators
`\/` and single-backslash classes `\d`/`\w`/`\t` are unaffected.
Contributor
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the fix ensures /regex/ string literals preserve escaped backslashes so the runtime and type-level parsers agree with the RegExp-instance form.
- Add
escapeEscapeparameter toScanner.shiftUntilEscapableto opt into preserving\\as a literal backslash. - Wire regex literals (
/andx/) in both runtime and type-levelparseEnclosedto passBackslash. - Leave quoted-string and date literal escaping behavior unchanged.
- Add regression tests for escaped-backslash parity, literal backslash, lone
\\, single-backslash classes, and escaped terminators.
Kimi K2 (free via Pullfrog for OSS) | 𝕏
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

A regex written as a string literal loses escaped backslashes, so a pattern that should match a literal backslash is compiled as a metacharacter class and validates the wrong values. The string-literal form and the equivalent
RegExp-instance form disagree, even though the docs treat them as equivalent.Repro
A regex matching a literal backslash followed by
dis/^\\d$/as aRegExp. Written as a string literal the backslashes double again, so it becomes"/^\\\\d$/".FromStringis compiled as the digit class\dinstead of a literal backslash plusd, so it rejects the value it should accept and accepts the value it should reject.FromInstance, which does not go through the string scanner, is correct, so the two forms diverge for the same intended pattern.Root cause
Both forms end in
new RegExp(source), but the string-literal path reads the source between the slashes withScanner.shiftUntilEscapable, which collapses every\\into a single\. That rule is correct for a JS string literal, where\\denotes one backslash. It is wrong for a regex source: there\\is a literal backslash while\d,\b,\ware metacharacters, so collapsing turns a literal backslash into a metacharacter and changes what the pattern accepts.Fix
Add an optional
escapeEscapeargument toshiftUntilEscapable. For regex tokens the parser emits\\instead of collapsing it, keeping the pattern source equal to theRegExp-instance form. The change is applied in both the runtime parser and its type-level twin, so runtime validation and inferred types stay in agreement. Quoted-string and date literals are unchanged and still collapse\\; escaped terminators (\/) and single-backslash classes (\d,\w,\t) are unaffected.Authority
A regex literal's content is a regex source, not a JS string, so
\\in it means one literal backslash. arktype's ownRegExp-instance path (type(/^\\d$/)) already preserves this, and the docs treattype("/regex/")as equivalent to the correspondingRegExp, so the string form must produce the same pattern. The string scanner instead applied JS-string un-escaping to the regex source, which is the divergence this fixes.Tests
Added cases in
ark/type/__tests__/regex.test.tscovering string-literal vsRegExp-instance parity, literal-backslash acceptance, a lone escaped backslash, and regression guards for single-backslash classes and escaped terminators. Verified red on the current base (the parity and backslash assertions fail without the parser change) and green with it. Full package suite passes (1774 passing; the 2 failingsnapPopulationcases are pre-existing under--skipTypesand unrelated to this change).