0.1.17 • Published 2 years ago

type-level-regexp v0.1.17

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

šŸ”¤šŸ” Type-Level RegExp (WIP)

npm version

TypeScript type-level RegExp parser and matcher implemented using template literals.

Demo

Open in Codeflow

šŸ‘‰ Try on TypeScript Playground 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
# Using pnpm
pnpm i -D type-level-regexp

# Using npm
npm i -D type-level-regexp
  1. 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.

import { createRegExp, spreadRegExpIterator } from 'type-level-regexp'

/** string.match() */
const regExp = createRegExp('foO(?<g1>b[a-g]r)(?:BAz|(?<g2>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})[-.](?<month>\\w{3,4})[-.](\\d{1,2})')
const replaceResult = '1991-Sept-15'.replace(regExp2, '$<month> $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.

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(?<g1>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 (created by the most inspiring, resourceful, and kind Daniel Roe from Nuxt, 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, and Gabriel Vergnaud's awesome hotscript very soon.

ā¤ļø Testing, feedbacks and PRs are welcome!

Features

  • Export createRegExp function to create aTypedRegExp 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, or see tests files in Tests and Stackblitz. (examples in index.test-d.ts)

Example - type-safe args in replacing function of string.replace()

replaceRegexp

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

type-level-matchAll-with-union

RegExp Tokens & Flags

TokensDescriptionSupport
.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, \SMatches any whitespace, non-whitespace character.āœ…
\d, \DMatches any digit, non-digit character.āœ…
\w, \WMatches any word, non-word character.āœ…
\b, \BMatches 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.āœ…
(?<name>)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.āœ…
(?<=), (?<!)Positive/Negative lookbehind.āœ…
FlagsDescriptionSupport
gGlobal matching (matches all occurrences).āœ…
iCase-insensitive matching.āœ…

šŸ’» Development

  • Clone this repository
  • Enable Corepack using corepack enable (use npm i -g corepack for Node.js < 16.10)
  • Install dependencies using pnpm install
  • Run interactive tests using pnpm dev

License

Made with šŸ”„ and ā¤ļø

Published under MIT License.

0.1.17

2 years ago

0.1.16

2 years ago

0.1.15

2 years ago

0.1.14

2 years ago

0.1.13

2 years ago

0.1.12

2 years ago

0.1.11

2 years ago

0.1.10

2 years ago

0.1.9

2 years ago

0.1.8

2 years ago

0.1.7

2 years ago

0.1.6

2 years ago

0.1.5

2 years ago

0.1.4

2 years ago

0.1.3

2 years ago

0.1.2

2 years ago

0.1.1

2 years ago

0.1.0

2 years ago

1.0.1

2 years ago