Skip to content

Commit 6131ac6

Browse files
authored
fix(types): infer group names from param with Input type (#32)
1 parent 2211a83 commit 6131ac6

File tree

3 files changed

+24
-6
lines changed

3 files changed

+24
-6
lines changed

src/core/inputs.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createInput, Input } from './internal'
22
import type { GetValue, EscapeChar } from './types/escape'
33
import type { Join } from './types/join'
4-
import type { MapToGroups, MapToValues, InputSource } from './types/sources'
4+
import type { MapToGroups, MapToValues, InputSource, GetGroup } from './types/sources'
55
import { IfSingle, wrap } from './wrap'
66

77
export type { Input }
@@ -49,19 +49,21 @@ export const not = {
4949
export const maybe = <New extends InputSource<string>>(str: New) =>
5050
createInput(`${wrap(exactly(str))}?`) as IfSingle<
5151
GetValue<New>,
52-
Input<`${GetValue<New>}?`>,
53-
Input<`(${GetValue<New>})?`>
52+
Input<`${GetValue<New>}?`, GetGroup<New>>,
53+
Input<`(${GetValue<New>})?`, GetGroup<New>>
5454
>
5555

5656
/** This escapes a string input to match it exactly */
57-
export const exactly = <New extends InputSource<string>>(input: New): Input<GetValue<New>> =>
57+
export const exactly = <New extends InputSource<string>>(
58+
input: New
59+
): Input<GetValue<New>, GetGroup<New>> =>
5860
typeof input === 'string'
5961
? (createInput(input.replace(/[.*+?^${}()|[\]\\/]/g, '\\$&')) as any)
6062
: input
6163

6264
export const oneOrMore = <New extends InputSource<string>>(str: New) =>
6365
createInput(`${wrap(exactly(str))}+`) as IfSingle<
6466
GetValue<New>,
65-
Input<`${GetValue<New>}+`>,
66-
Input<`(${GetValue<New>})+`>
67+
Input<`${GetValue<New>}+`, GetGroup<New>>,
68+
Input<`(${GetValue<New>})+`, GetGroup<New>>
6769
>

src/core/types/sources.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import type { Input } from '../internal'
22
import type { GetValue } from './escape'
33

44
export type InputSource<S extends string = never, T extends string = never> = S | Input<S, T>
5+
export type GetGroup<T extends InputSource<string>> = T extends Input<string, infer Group>
6+
? Group
7+
: never
58
export type MapToValues<T extends InputSource<any, any>[]> = T extends [infer First, ...infer Rest]
69
? First extends InputSource<string>
710
? [GetValue<First>, ...MapToValues<Rest>]

test/inputs.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
carriageReturn,
2222
charNotIn,
2323
} from '../src/core/inputs'
24+
import { createRegExp, MagicRegExp } from '../src'
2425

2526
describe('inputs', () => {
2627
it('charIn', () => {
@@ -54,19 +55,31 @@ describe('inputs', () => {
5455
const regexp = new RegExp(input as any)
5556
expect(regexp).toMatchInlineSnapshot('/\\(foo\\)\\?/')
5657
expectTypeOf(extractRegExp(input)).toEqualTypeOf<'(foo)?'>()
58+
const nestedInputWithGroup = maybe(exactly('foo').as('groupName'))
59+
expectTypeOf(createRegExp(nestedInputWithGroup)).toEqualTypeOf<
60+
MagicRegExp<'/((?<groupName>foo))?/', 'groupName', never>
61+
>()
5762
})
5863
it('oneOrMore', () => {
5964
const input = oneOrMore('foo')
6065
const regexp = new RegExp(input as any)
6166
expect(regexp).toMatchInlineSnapshot('/\\(foo\\)\\+/')
6267
expectTypeOf(extractRegExp(input)).toEqualTypeOf<'(foo)+'>()
68+
const nestedInputWithGroup = oneOrMore(exactly('foo').as('groupName'))
69+
expectTypeOf(createRegExp(nestedInputWithGroup)).toEqualTypeOf<
70+
MagicRegExp<'/((?<groupName>foo))+/', 'groupName', never>
71+
>()
6372
})
6473
it('exactly', () => {
6574
const input = exactly('fo?[a-z]{2}/o?')
6675
expect(new RegExp(input as any)).toMatchInlineSnapshot(
6776
'/fo\\\\\\?\\\\\\[a-z\\\\\\]\\\\\\{2\\\\\\}\\\\/o\\\\\\?/'
6877
)
6978
expectTypeOf(extractRegExp(input)).toEqualTypeOf<'fo\\?\\[a-z\\]\\{2\\}\\/o\\?'>()
79+
const nestedInputWithGroup = exactly(maybe('foo').and('bar').as('groupName'))
80+
expectTypeOf(createRegExp(nestedInputWithGroup)).toEqualTypeOf<
81+
MagicRegExp<'/(?<groupName>(foo)?bar)/', 'groupName', never>
82+
>()
7083
})
7184
it('word', () => {
7285
const input = word

0 commit comments

Comments
 (0)