https://github.com/didavid61202/type-level-regexp
🔤🔍 Type-level RegExp, parse and match string in TypeScript type system.
https://github.com/didavid61202/type-level-regexp
javascript parser regex regexp types typescript
Last synced: about 1 year ago
JSON representation
🔤🔍 Type-level RegExp, parse and match string in TypeScript type system.
- Host: GitHub
- URL: https://github.com/didavid61202/type-level-regexp
- Owner: didavid61202
- License: mit
- Created: 2023-02-22T17:47:10.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2023-06-15T09:25:36.000Z (about 3 years ago)
- Last Synced: 2025-03-30T15:11:45.559Z (about 1 year ago)
- Topics: javascript, parser, regex, regexp, types, typescript
- Language: TypeScript
- Homepage: https://tsplay.dev/mbvEEm
- Size: 450 KB
- Stars: 113
- Watchers: 2
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# 🔤🔍 Type-Level RegExp (WIP)
[![npm version][npm-version-src]][npm-version-href]
> TypeScript type-level RegExp parser and matcher implemented using template literals.

[](https://stackblitz.com/~/github.com/didavid61202/type-level-regexp)
👉 [Try on TypeScript Playground](https://www.typescriptlang.org/play?target=99&jsx=0#code/PQKhCgAIUqYMQPYCdIFsUFNKYB4EM0AHAG0wGdJ8A7AEypJMnIFciiUAXTegJUwDmAUVxFIRfJ27JqlGvRbl8AzABo44svnLYdmAFyQAFlKLl9wYAICWnIywBGAOgDGiNMFrXa+AG7eANgBGACYABhDgTgBPIkwAWjJfTBJ45EE8Ig04YHBwS0gAIW1rF0hFZQNIa2IuKjpynUgAAxd0yUx+YVEACgByAB4ukTEJKUwZAD4+gEpm6upNfBdsRAAzSGjEFlRqFDR8JmAhwRHxSWlqSeBIYdFIRAcAK0wXTidwGo5kTkgAb0gbUwHTuYgAvpA1sh3JAAEQxOKJTDJVLpFSiWEAblgBQAhPjIAAFLRNADu+FskMwpOYr0QdEoaxQkAAKgBlJbRATQlgNTiIKmcFxGSB9BEJJIpNIZUR9SHQtCQahERXySDpKEUEV2bDihZrCaYagrSD43F5UAQaCQNmcZDWagCJwHIVGHozHAEYhkHLgNyyX5os4AXkB7W4oJ6sKZiB6AH4BgIgpMHPgANoCNIAXRm8f0PVTAC8ZgAfByIRAzOPx4MARxYuBmsNUkDTsOssJzmPyN0gfcgAD043l-eRfi7hfxWCRfqH4RROIY1gB5R74XjlxAARQbsOdkmFPSDohm3YK-cHw-AE6MU5YM7TYSzZ97F4vQ7yN7vD6Cz57b7fD8vwoe9ODTEI-3PAC+yAg9bxAh8AGZINfaDYNdb93gdWg8BfaD+3QycEPeMhHTsPD8Mva84MwpxuW2MwKPwj98jADRbXtR0nHSUhlkwd1PUIUhMF9Udfh8bgOIdARIFDPpYRZIxsB8aJICCABOdSgniMJ1PiIIAFZqjkG1eRU2E+m4zBeJWHooEgKCqL7IEQVOXp7L7WF4wGYMAB1fIAdVzBMiA1axcEmPN-NJEt-PIGYQDjYKBmiYFkEmfzaD+AAWMEZnibyMGoOwMt8rKQjygqExU0ryry7zWDWNZwsiuN9Gi2LfPikAZn8gLYXsmZ1D7OyLxvYb+1CzBmtwCa+wKD8L1S-BkDmhybkW-sirsNaVLWxqZrW9YDR0Tg1oW4cLwdIgWDOjzIHothyHui7BtkyZ7uaABJRY0wAEj+a7brBLMWxvSAAZvCFJAWHDcEMAHjooTBOAhaxKC+tll0gAAOAIwiCJxARoBhyAFJlkBdKhzAhv4ppmsEAceswnBUsFgEh+k7HZpmeRZ5bkEZv4DvCiEehU2TabZ+p6AFyWAYFvLmnAU8LTY612RwZBoVQawNgdXxDm8W43LEchomK-BZuMRBklQflaWUlGKSYCYdfQCglBUD5oFyMSFkNkhvFBSWXIjU2oxjAsVthVXHKooA)
or see examples in `Playground` and `test` folders.
🚧 Work In Progress, PRs and issues are welcome 🚧
## Quick Setup
1. Add `type-level-regexp` dependency to your project
```bash
# Using pnpm
pnpm i -D type-level-regexp
# Using npm
npm i -D type-level-regexp
```
2. Import `createRegExp` function, pass in a RegExp string pattern to it creates a `TypedRegExp`, passing this `TypedRegExp` to `String.match()`, `String.matchAll()` or `String.replace()` functions to get fully typed match result.
## Basic Usage
match result will be fully typed if match against a literal stirng, or shows emumerated results if match against a dynamic string.
```ts
import { createRegExp, spreadRegExpIterator } from 'type-level-regexp'
/** string.match() */
const regExp = createRegExp('foO(?b[a-g]r)(?:BAz|(?qux))', ['i'])
const matchResult = 'prefix foobarbaz suffix'.match(regExp) // matching literal string
matchResult[0] // 'foobarbaz'
matchResult[1] // 'bar'
matchResult[3] // show type error `type '3' can't be used to index type 'RegExpMatchResult<...>`
matchResult.length // 3
matchResult.index // 7
matchResult.groups // { g1: "bar"; g2: undefined; }
/** string.replace() */
const regExp2 = createRegExp('(\\d{4})[-.](?\\w{3,4})[-.](\\d{1,2})')
const replaceResult = '1991-Sept-15'.replace(regExp2, '$ $3, $1')
replaceResult // 'Sept 15, 1991'
/** string.matchAll() */
const regExp3 = createRegExp('c[a-z]{2}', ['g'])
const matchALlIterator = 'cat car caw cay caw cay'.matchAll(regExp3)
const spreadedResult = spreadRegExpIterator(matchALlIterator)
spreadedResult[2][0] // 'caw'
spreadedResult[3].index // 12
const InvalidRegExp = createRegExp('foo(bar')
// TypeScript error: Argument of type 'string' is not assignable to parameter of type 'RegExpSyntaxError<"Invalid regular expression, missing closing \`)\`">'
```
For TypeScript library authors, you can also import individual generic types to parse and match RegExp string at type-level and combine with your library's type-level features.
```ts
import { ParseRegExp, MatchRegExp } from 'type-level-regexp'
type MatchResult = MatchRegExp<'fooBAR42', ParseRegExp<'Fo[a-z](Bar)\\d{2}'>, 'i'>
type Matched = MatchResult[0] // 'fooBAR42'
type First = MatchResult[1] // 'BAR'
type RegExpAST = ParseRegExp<'foo(?bar)'>
// [{
// type: "string";
// value: "foo";
// }, {
// type: "namedCapture";
// name: "g1";
// value: [{
// type: "string";
// value: "bar";
// }];
// }]
```
## Origin & Notice
The main purpose of this project is to test and demonstrate the possibility and limitations of writing a RegExp parser/matcher in TypeScript's type-level. Note that this may not be practically useful, but rather an interesting showcase.
The idea for this project originated while I was working on improving the type hints of string.match and replace in [magic-regexp](https://github.com/danielroe/magic-regexp) (created by the most inspiring, resourceful, and kind [Daniel Roe](https://github.com/danielroe) from [Nuxt](https://nuxt.com), definitely check it out if you are working with RegExp and TypeScript!).
As the complexity grows, I start working on this separated repo to increase development speed and try out different iterations. It will be incorporate and use in [magic-regexp](https://github.com/danielroe/magic-regexp), and [Gabriel Vergnaud](https://github.com/gvergnaud)'s awesome [hotscript](https://github.com/gvergnaud/hotscript) very soon.
❤️ Testing, feedbacks and PRs are welcome!
## Features
- Export `createRegExp` function to create a`TypedRegExp` that replace your original `/regex_pattern/` regex object, which can be pass to `String.match()`, `String.matchAll()` and `String.replace()` functions and gets fully typed result.
- Shows `RegExpSyntaxError` if the provided RegExp pattern is invalid.
- Enhance types of RegExp related `String` functions (`.match`, `matchAll`, `.replace`...) for literal or dynamic typed string.
- Result of `String` functions matched exactly as runtime result.
- Support all common RegExp tokens (incl. Lookarounds, Backreferences...etc), quantifiers (incl. greedy/lazy) and (`g`,`i`) flags.
- Export helper functions `spreadRegExpMatchArray` and `spreadRegExpIterator` to get tuple type of match results and iterators.
- Provide generic type `ParseRegExp` to parse and RegExp string to AST.
- Provide generic type `MatchRegExp` to match giving string with a parsed RegExp.
- Provide generic type `ResolvePermutation` to permutation all possible matching string of given RegExp if possible (due to TypeScript type-level limitation)
- More details please [try on TypeScript Playground](https://www.typescriptlang.org/play?target=99&jsx=0#code/PQKhCgAIUqYMQPYCdIFsUFNKYB4EM0AHAG0wGdJ8A7AEypJMnIFciiUAXTegJUwDmAUVxFIRfJ27JqlGvRbl8AzABo44svnLYdmAFyQAFlKLl9wYAICWnIywBGAOgDGiNMFrXa+AG7eANgBGACYABhDgTgBPIkwAWjJfTBJ45EE8Ig04YHBwS0gAIW1rF0hFZQNIa2IuKjpynUgAAxd0yUx+YVEACgByAB4ukTEJKUwZAD4+gEpm6upNfBdsRAAzSGjEFlRqFDR8JmAhwRHxSWlqSeBIYdFIRAcAK0wXTidwGo5kTkgAb0gbUwHTuYgAvpA1sh3JAAEQxOKJTDJVLpFSiWEAblgBQAhPjIAAFLRNADu+FskMwpOYr0QdEoaxQkAAKgBlJbRATQlgNTiIKmcFxGSB9BEJJIpNIZUR9SHQtCQahERXySDpKEUEV2bDihZrCaYagrSD43F5UAQaCQNmcZDWagCJwHIVGHozHAEYhkHLgNyyX5os4AXkB7W4oJ6sKZiB6AH4BgIgpMHPgANoCNIAXRm8f0PVTAC8ZgAfByIRAzOPx4MARxYuBmsNUkDTsOssJzmPyN0gfcgAD043l-eRfi7hfxWCRfqH4RROIY1gB5R74XjlxAARQbsOdkmFPSDohm3YK-cHw-AE6MU5YM7TYSzZ97F4vQ7yN7vD6Cz57b7fD8vwoe9ODTEI-3PAC+yAg9bxAh8AGZINfaDYNdb93gdWg8BfaD+3QycEPeMhHTsPD8Mva84MwpxuW2MwKPwj98jADRbXtR0nHSUhlkwd1PUIUhMF9Udfh8bgOIdARIFDPpYRZIxsB8aJICCABOdSgniMJ1PiIIAFZqjkG1eRU2E+m4zBeJWHooEgKCqL7IEQVOXp7L7WF4wGYMAB1fIAdVzBMiA1axcEmPN-NJEt-PIGYQDjYKBmiYFkEmfzaD+AAWMEZnibyMGoOwMt8rKQjygqExU0ryry7zWDWNZwsiuN9Gi2LfPikAZn8gLYXsmZ1D7OyLxvYb+1CzBmtwCa+wKD8L1S-BkDmhybkW-sirsNaVLWxqZrW9YDR0Tg1oW4cLwdIgWDOjzIHothyHui7BtkyZ7uaABJRY0wAEj+a7brBLMWxvSAAZvCFJAWHDcEMAHjooTBOAhaxKC+tll0gAAOAIwiCJxARoBhyAFJlkBdKhzAhv4ppmsEAceswnBUsFgEh+k7HZpmeRZ5bkEZv4DvCiEehU2TabZ+p6AFyWAYFvLmnAU8LTY612RwZBoVQawNgdXxDm8W43LEchomK-BZuMRBklQflaWUlGKSYCYdfQCglBUD5oFyMSFkNkhvFBSWXIjU2oxjAsVthVXHKooA), or see tests files in [Tests](./test) and [Stackblitz](https://stackblitz.com/~/github.com/didavid61202/type-level-regexp). (examples in index.test-d.ts)
#### Example - type-safe args in replacing function of `string.replace()`

#### Example - spreaded `string.matchAll()` with union of RegExp pattern remain as tuple

### RegExp Tokens & Flags
| Tokens | Description | Support |
| --- | --- | --- |
| `.` | Matches any single character. | ✅ |
| `*`, `*?` | Matches zero or more occurrences (Greedy/Lazy). | ✅ |
| `+`, `*?` | Matches one or more occurrences (Greedy/Lazy). | ✅ |
| `?`, `??` | Matches zero or one occurrence (Greedy/Lazy). | ✅ |
| `^` | Matches the start of a line. | ✅ |
| `$` | Matches the end of a line. | ✅ |
| `\s`, `\S` | Matches any whitespace, non-whitespace character. | ✅ |
| `\d`, `\D` | Matches any digit, non-digit character. | ✅ |
| `\w`, `\W` | Matches any word, non-word character. | ✅ |
| `\b`, `\B` | Matches a word-boundary, non-word-boundary. | ✅ |
| `[abc]` | Matches any character in the set. | ✅ |
| `[^abc]` | Matches any character not in the set. | ✅ |
| `()` | Creates a capturing group. | ✅ |
| `(?:)` | Creates a non-capturing group. | ✅ |
| `(?)` | Creates a named-capturing group. | ✅ |
| `\|` | Matches either the expression before or after the vertical bar. | ✅ |
| `{n}` | Matches exactly `n` occurrences. | ✅ |
| `{n,}` | Matches at least `n` occurrences. | ✅ |
| `{n,m}` | Matches between `n` and `m` occurrences. | ✅ |
| `(?=)`, `(?!)` | Positive/Negative lookahead. | ✅ |
| `(?<=)`, `(?
[npm-version-src]: https://img.shields.io/npm/v/type-level-regexp?style=flat-square
[npm-version-href]: https://npmjs.com/package/type-level-regexp