Repository navigation
RegExp literal flag types #38671
Description
Search Terms
RegExp Regex literal flags as const narrowing
Suggestion
This is similar to the proposal for typing named capture groups #32098
It would be useful to narrow RegExp literals based on their flags as well. This could be modeled as type parameter(s) as in that proposal, or as a type intersection, which I will use here.
const myRegExp = /hello/y; // has type RegExp & {dotAll: false, global: false, ignoreCase: false, multiline: false, sticky: true, unicode: false, flags: 'y'};
Use Cases
Regular expressions have different behavior when defined with different flags, and functions may have different behavior when they receive them, so they may want to distinguish the flags of a RegExp for overloads or allowed argument values.
Examples
In the standard library, String.prototype.match returns either the first match with its capture groups, or a list of all matches, depending on whether the argument is a global RegExp. This could be modeled as an overload:
match(str: string): RegExpMatchArray|null
match(regexp: RegExp & {global: false}): RegExpMatchArray|null
match(regexp: RegExp & {global: true}): Array<string>|null;
match(regexp: RegExp): RegExpMatchArray|null;
I recently wrote some functions which only work with sticky RegExps, so I had to put checks at the top of each function:
function match(regexp: RegExp) {
if (!regexp.sticky) { throw new Error("Invalid argument -- RegExp must use the /y 'sticky' flag"); }
...
}
match(/sticky example/y); // compiles and runs fine
match(/non-sticky example/); // compiles fine, runtime errorI'd like to define the function instead as:
type StickyRegExp = RegExp & {sticky: true};
function match(regexp: StickyRegExp) {
...
}
match(/sticky example/y); // compiles and runs fine
match(/non-sticky example/); // compiler errorChecklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.
Activity
webstrand commentedon Jun 17, 2021
I've implemented the
as constmodifier for RegExp literals (https://github.com/webstrand/TypeScript/tree/regexp-literal-flags). This does not include support forconst foo = /foo/greceiving the typeRegExp & { global: true, ignoreCase: false, multiline: false, ... }. Sinceas constwas not previously permitted on RegExp literals, this is not a breaking change.I played around with adapting the type-freshness system to include regular expression literals, but it looks like that system was never designed to interact with non-literal types. Changing this would involve either adding
regexps as a primitive type or comprehensive changes to the freshness system to allow non-literal types to have fresh types.I would be willing to continue to work on this feature, if there's any continuing interest in it? I'll probably need a little guidance, so I don't make a mess of things. It looks like this needs to be added to the backlog, before I can make a pull request.
Tyler-Murphy commentedon Sep 20, 2021
This would also be useful for
String.prototype.replaceAll, which throws a runtime error if the first argument is a non-global regular expression. See eslint/eslint#15073 for more context.codpro2005 commentedon Jul 22, 2025
I wrote a custom library ts-regexp that seems to mostly address this issue. That said, it would be cool if this was possible natively, though the heavy compilation may discourage it.