@@ -237,8 +237,10 @@ type Groups = (Token & {type: 'groups'})['groups'];
237237// Indexification (DFS)
238238// Utils (DFS math)
239239type FlattenGroupsDeep < TGroups extends Groups > = unknown extends AsLinked < TGroups , infer First , infer Rest >
240- // @ts -expect-error: TS cannot infer that 'Rest' extends 'Groups'
241- ? [ First , ...FlattenTokenDeep < First [ 'inner' ] > , ...FlattenGroupsDeep < Rest > ]
240+ ? [ First , ...FlattenTokenDeep < First [ 'inner' ] > , ...FlattenGroupsDeep <
241+ // @ts -expect-error: TS cannot infer that this extends 'Groups'
242+ Rest
243+ > ]
242244 : [ ]
243245;
244246type FlattenTokenDeep < TToken extends Token > = TToken extends { type : 'alternation' } ? [
@@ -255,16 +257,23 @@ type IndexifyGroupsDeep<TGroups extends Groups, TIndex extends number> = unknown
255257 index : TIndex ,
256258 value : Omit < First , 'inner' > & { inner : IndexifyTokenDeepInternal < First [ 'inner' ] , Increment < TIndex > > }
257259 } ,
258- // @ts -expect-error: TS cannot infer that 'Rest' extends 'Groups'
259- ...IndexifyGroupsDeep < Rest , Add < TIndex , FlattenGroupsDeep < [ First ] > [ 'length' ] & number > >
260+ ...IndexifyGroupsDeep <
261+ // @ts -expect-error: TS cannot infer that this extends 'Groups'
262+ Rest ,
263+ Add < TIndex , FlattenGroupsDeep < [ First ] > [ 'length' ] & number >
264+ >
260265 ]
261266 : [ ]
262267;
263268type IndexifyTokenDeepInternal < TToken extends Token , TIndex extends number > = TToken extends { type : 'alternation' } ? {
264269 type : 'alternation' ,
265270 left : IndexifyTokenDeepInternal < TToken [ 'left' ] , TIndex > ,
266- // @ts -expect-error: 'FlattenTokenDeep<TToken['left']>['length']' should terminate
267- right : IndexifyTokenDeepInternal < TToken [ 'right' ] , Add < TIndex , FlattenTokenDeep < TToken [ 'left' ] > [ 'length' ] & number > >
271+ right : IndexifyTokenDeepInternal < TToken [ 'right' ] , Add <
272+ TIndex ,
273+ // @ts -expect-error: this should terminate
274+ FlattenTokenDeep < TToken [ 'left' ] > [ 'length' ]
275+ & number
276+ > >
268277} : TToken extends { type : 'groups' } ? {
269278 type : 'groups' ,
270279 groups : IndexifyGroupsDeep < TToken [ 'groups' ] , TIndex >
@@ -302,8 +311,10 @@ type ContextualValue<T extends GroupWithIndex, TValue> = Record<T['index'], {
302311 reference : T [ 'value' ]
303312} > ;
304313type DeepUndefinedGroups < TGroups extends GroupWithIndexes > = unknown extends AsLinked < TGroups , infer First , infer Rest >
305- // @ts -expect-error: TS cannot infer that 'Rest' extends 'GroupWithIndexes'
306- ? ContextualValue < First , undefined > & DeepUndefinedToken < First [ 'value' ] [ 'inner' ] > & DeepUndefinedGroups < Rest >
314+ ? ContextualValue < First , undefined > & DeepUndefinedToken < First [ 'value' ] [ 'inner' ] > & DeepUndefinedGroups <
315+ // @ts -expect-error: TS cannot infer that this extends 'GroupWithIndexes'
316+ Rest
317+ >
307318 : { }
308319;
309320type DeepUndefinedToken < TToken extends TokenWithIndex > = TToken extends { type : 'alternation' }
@@ -318,8 +329,10 @@ type ContextualizeGroups<TGroups extends GroupWithIndexes> = unknown extends AsL
318329 ? DeepUndefinedGroups < [ First ] >
319330 : never
320331 ) | ( ContextualValue < First , string > & ContextualizeToken < First [ 'value' ] [ 'inner' ] > )
321- // @ts -expect-error: TS cannot infer that 'Rest' extends 'GroupWithIndexes'
322- ) & ContextualizeGroups < Rest >
332+ ) & ContextualizeGroups <
333+ // @ts -expect-error: TS cannot infer that this extends 'GroupWithIndexes'
334+ Rest
335+ >
323336 : { }
324337;
325338type ContextualizeToken < TToken extends TokenWithIndex > = TToken extends { type : 'alternation' }
@@ -357,7 +370,7 @@ type Parse<T extends string> = string extends T
357370 captures : [ string , ...( string | undefined ) [ ] ] ,
358371 namedCaptures : Record < string , string | undefined > ;
359372 }
360- // @ts -expect-error: 'Distribute<ContextualizeToken<IndexifyTokenDeep<TokenTree<`(${T})`>>>>' should terminate
373+ // @ts -expect-error: this should terminate
361374 : Distribute < ContextualizeToken < IndexifyTokenDeep < TokenTree < `(${T } )`> > > >
362375;
363376
@@ -373,8 +386,11 @@ type GetFlagsInternal<T extends string, TFlags extends Flag[]> = unknown extends
373386 ? `${FirstMatch < First , T > extends never
374387 ? ''
375388 : FirstMatch < First , T >
376- // @ts -expect-error: TS cannot infer that 'Rest' extends 'Flag[]'
377- } ${GetFlagsInternal < T , Rest > } `
389+ } ${GetFlagsInternal <
390+ T ,
391+ // @ts -expect-error: TS cannot infer that this extends 'Flag[]'
392+ Rest
393+ > } `
378394 : ''
379395;
380396type GetFlags < T extends string > = string extends T
@@ -383,8 +399,11 @@ type GetFlags<T extends string> = string extends T
383399;
384400type AreFlagsValid < TSource extends string , TFlags extends Flag [ ] > = TSource extends `${infer First } ${infer Rest } `
385401 ? First extends TFlags [ number ]
386- // @ts -expect-error: TS cannot infer that 'Remove<TFlags, First>' extends 'Flag[]'
387- ? AreFlagsValid < Rest , Remove < TFlags , First > >
402+ ? AreFlagsValid <
403+ Rest ,
404+ // @ts -expect-error: TS cannot infer that this extends 'Flag[]'
405+ Remove < TFlags , First >
406+ >
388407 : false
389408 : true
390409;
@@ -561,4 +580,114 @@ export const typedRegExp = <
561580 & ( GlobalFalseIndicesBehavior < false > | GlobalFalseIndicesBehavior < true > )
562581 )
563582 ) & ( IndicesBehavior < false > | IndicesBehavior < true > ) > ;
564- } ;
583+ } ;
584+
585+ typedRegExp ( '?<named>' ) . matchIn ( '(?<named>)' ) ! . groups ; // HANDLE!
586+
587+ // #region Runtime tests
588+
589+ // #region Version tests
590+
591+ // // < es2018
592+ const normalGroups = new RegExp ( '' ) . exec ( '' ) ! . groups ;
593+ const typedGroups = typedRegExp ( '' ) . exec ( '' ) ! . groups ;
594+ // // < es2022
595+ const normalHasIndices = new RegExp ( '' ) . hasIndices ;
596+ const typedHasIndices = typedRegExp ( '' ) . hasIndices ;
597+ const normalIndices = new RegExp ( '' ) . exec ( '' ) ! . indices ;
598+ const typedIndices = typedRegExp ( '' , 'd' ) . exec ( '' ) ! . indices ;
599+
600+ // #endregion
601+
602+ // #region General tests
603+
604+ const check = typedRegExp ( '' ) [ Symbol . match ] ( '' ) ; // Error!
605+
606+ const conditionFlagsSuccess = typedRegExp ( '(?<hello>)(?<world>)' , 'd' ) . exec ( '' ) ! . indices ;
607+ const conditionFlagsFail = typedRegExp ( '(?<hello>)(?<world>)' , '' ) . exec ( '' ) ! . indices ; // Error!
608+
609+ const gettingFlags = typedRegExp ( 'lolxd' , 'gid' ) . flags ; // 'dgi'
610+ const gettingFlagsNonLiteral = typedRegExp ( 'lolxd' , 'gid' as string ) . flags ; // string
611+ const gettingSource = typedRegExp ( 'lolxd' , 'gid' ) . source ; // 'lolxd'
612+ const gettingSourceEmpty = typedRegExp ( '' , 'gid' ) . source ; // '(?:)'
613+ const getTypedRegExpNonLiteralSource = < const TFlags extends string > ( flags : ValidatedFlags < TFlags > ) => typedRegExp ( '(?<namedGroup>))' as string , flags ) ;
614+ const typedRegExpNonLiteralSourceGlobalFlags = getTypedRegExpNonLiteralSource ( 'gid' ) ;
615+ const gettingSourceNonLiteral0 = typedRegExpNonLiteralSourceGlobalFlags . exec ( '' ) ! [ 0 ] ; // string
616+ const gettingSourceNonLiteralIndex = typedRegExpNonLiteralSourceGlobalFlags . exec ( '' ) ! [ 34 ] ; // string | undefined
617+ const gettingSourceNonLiteral = typedRegExpNonLiteralSourceGlobalFlags . exec ( '' ) ! . groups ; // RegExp['groups']
618+ const gettingSourceNonLiteralMatchGlobal = typedRegExpNonLiteralSourceGlobalFlags . matchIn ( '' ) ; // [string, ...string[]] | null
619+ const gettingSourceNonLiteralMatchIndices = getTypedRegExpNonLiteralSource ( 'id' ) . matchIn ( '' ) ; // StrictRegExpExecArrayForHasIndices<true> | null
620+ const gettingSourceNonLiteralMatch = getTypedRegExpNonLiteralSource ( 'i' ) . matchIn ( '' ) ; // StrictRegExpExecArrayForHasIndices<false> | null
621+ const gettingSourceNonLiteralReplace = typedRegExpNonLiteralSourceGlobalFlags . replaceIn ( 'source' , ( ...args ) => {
622+ const match = args [ 0 ] ; // string
623+ const rest = args [ 1 ] ; // string | number | Record<string, string | undefined> | undefined
624+ return '' ;
625+ } ) ;
626+ const matched1 = typedRegExp ( '(?<hello>)(?<world>)' , 'dg' ) . matchIn ( '' ) ! [ 0 ] ; // string
627+ const matched2 = typedRegExp ( '(?<hello>)(?<world>)' , 'dg' ) . matchIn ( '' ) ! [ 1 ] ; // string | undefined
628+ const replacedThroughValue = typedRegExp ( '((?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2}))?' , '' ) . replaceIn ( '' , 'replacement-value' ) ;
629+ const replaceThroughReplacer = typedRegExp ( '(?<main>..(?<nested>.))?' , '' ) . replaceIn ( '' , ( match , main , nested , offset , string , groups ) => groups . nested ! ) ;
630+ const replaceThroughReplacerNotAllArgs = typedRegExp ( '(?<main>..(?<nested>.))?' , '' ) . replaceIn ( '' , ( match , main , nested , offset , string , ...rest ) => nested ! ) ; // todo (probably nothing that can be done): rest as destructured tuple instead of full Array?
631+
632+ const interesting = typedRegExp ( '(?<a>...)|(?<b>...(?<bInner>))?|(?<c>...)' ) . exec ( 'dffdf' ) ! ;
633+ const interestingOut = interesting . groups . a ;
634+ if ( interesting . groups . bInner === undefined && interesting . groups . c === undefined ) {
635+ const interstingIn = interesting . groups . a ; // string | undefined
636+ }
637+
638+ const myTest = typedRegExp ( '(?<main>.(?<nested>.))?' ) . exec ( '' ) ! . groups ;
639+ const main = myTest . main ; // string | undefined
640+ if ( myTest . nested === undefined ) {
641+ const inner = myTest . main ; // undefined
642+ }
643+
644+ const c = typedRegExp ( '(?<main>(?<nested>))?' , 'g' as string ) ;
645+ for ( const match of 'matchAllIn' in c ? c . matchAllIn ( '' ) : [ ] ) {
646+ type Out = typeof match . groups . nested ;
647+ if ( match . groups . main ) {
648+ type In = typeof match . groups . nested ;
649+ const haa = match . indices ; // should error
650+ const lastGroup = match [ 3 ] ; // should error
651+ }
652+ `Found ${ match [ 0 ] } start=${ match . index } end=${ match . index + match [ 0 ] . length } .` ;
653+ }
654+
655+ // #endregion
656+
657+ // #region Non-literal tests
658+
659+ const nonLiteralFlags = typedRegExp ( '(?<hello>)(?<world>)' , 'dg' as string ) ;
660+ if ( nonLiteralFlags . hasIndices ) {
661+ const inds = nonLiteralFlags . exec ( '' ) ! . indices ; // RegExpIndicesArray
662+ const inds2 = nonLiteralFlags . matchIn ( '' ) ! . indices ; // Error!
663+ if ( ! nonLiteralFlags . global ) {
664+ const inds2_2 = nonLiteralFlags . matchIn ( '' ) ! . indices ; // RegExpIndicesArray
665+ } else {
666+ for ( const l of nonLiteralFlags . matchAllIn ( '' ) ) {
667+ l . indices ; // RegExpIndicesArray
668+ }
669+ }
670+ }
671+ const mtch = nonLiteralFlags . matchIn ( '' ) ; // StrictRegExpExecArray | [string, ...string[]] | null
672+ if ( nonLiteralFlags . global ) {
673+ const inmtch = nonLiteralFlags . matchIn ( '' ) ; // [string, ...string[]] | null
674+ for ( const inmtch2 of nonLiteralFlags . matchAllIn ( '' ) ) {
675+ inmtch2 . indices ; // Error!
676+ if ( 'indices' in inmtch2 ) {
677+ const indices = inmtch2 . indices ; // RegExpIndicesArray
678+ }
679+ }
680+ const rplcAll = nonLiteralFlags . replaceAllIn ( '' , '' ) ;
681+ } else {
682+ const inmtch = nonLiteralFlags . matchIn ( '' ) ! ; // StrictRegExpExecArray | null
683+ inmtch . indices ; // Error!
684+ if ( 'indices' in inmtch ) {
685+ const indices = inmtch . indices ; // RegExpIndicesArray
686+ }
687+ }
688+
689+ // #endregion
690+
691+ // #endregion
692+
693+ // #endregion
0 commit comments