Skip to content

Commit 833ca50

Browse files
committed
scoping 'ts-expect-error's to problematic part
1 parent 513066e commit 833ca50

File tree

1 file changed

+145
-16
lines changed

1 file changed

+145
-16
lines changed

src/index.ts

Lines changed: 145 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,10 @@ type Groups = (Token & {type: 'groups'})['groups'];
237237
// Indexification (DFS)
238238
// Utils (DFS math)
239239
type 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
;
244246
type 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
;
263268
type 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
}>;
304313
type 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
;
309320
type 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
;
325338
type 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
;
380396
type GetFlags<T extends string> = string extends T
@@ -383,8 +399,11 @@ type GetFlags<T extends string> = string extends T
383399
;
384400
type 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

Comments
 (0)