@aidc-toolkit/utility 0.0.1 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Describe the bug**
11
+ A clear and concise description of what the bug is.
12
+
13
+ **To Reproduce**
14
+ Steps to reproduce the behavior:
15
+ 1. Go to '...'
16
+ 2. Click on '....'
17
+ 3. Scroll down to '....'
18
+ 4. See error
19
+
20
+ **Expected behavior**
21
+ A clear and concise description of what you expected to happen.
22
+
23
+ **Screenshots**
24
+ If applicable, add screenshots to help explain your problem.
25
+
26
+ **Platform**
27
+ - OS: [e.g. Windows, iOS]
28
+ - Browser [e.g. chrome, safari]
29
+ - Browser Version [e.g. 22]
30
+
31
+ **Additional context**
32
+ Add any other context about the problem here.
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Is your feature request related to a problem? Please describe.**
11
+ A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12
+
13
+ **Describe the solution you'd like**
14
+ A clear and concise description of what you want to happen.
15
+
16
+ **Describe alternatives you've considered**
17
+ A clear and concise description of any alternative solutions or features you've considered.
18
+
19
+ **Additional context**
20
+ Add any other context or screenshots about the feature request here.
@@ -16,6 +16,12 @@ jobs:
16
16
  with:
17
17
  node-version: 22
18
18
  - run: npm ci
19
+ # This is necessary to work around platform-specific optional dependencies bug (https://github.com/npm/cli/issues/4828).
20
+ # - run: npm i @rollup/rollup-linux-x64-gnu
21
+ # "vitest run" fails with:
22
+ # Error: failed to resolve "extends":"@aidc-toolkit/dev/tsconfig.json" in /home/runner/work/utility/utility/tsconfig.json
23
+ # Caused by: Error: Cannot find module '@aidc-toolkit/dev/tsconfig.json/tsconfig.json'
24
+ # - run: npm test
19
25
 
20
26
  publish-npm:
21
27
  needs: build
@@ -0,0 +1,12 @@
1
+ <component name="ProjectRunConfigurationManager">
2
+ <configuration default="false" name="build-dev" type="js.build_tools.npm" nameIsGenerated="true">
3
+ <package-json value="$PROJECT_DIR$/package.json" />
4
+ <command value="run" />
5
+ <scripts>
6
+ <script value="build-dev" />
7
+ </scripts>
8
+ <node-interpreter value="project" />
9
+ <envs />
10
+ <method v="2" />
11
+ </configuration>
12
+ </component>
package/eslint.config.js CHANGED
@@ -6,6 +6,9 @@ import esLintConfigLove from "eslint-config-love";
6
6
  import { esLintConfigAIDCToolkit } from "@aidc-toolkit/dev";
7
7
 
8
8
  export default tseslint.config(
9
+ {
10
+ ignores: ["eslint.config.js", "dist"]
11
+ },
9
12
  js.configs.recommended,
10
13
  ...tseslint.configs.strictTypeChecked,
11
14
  stylistic.configs["recommended-flat"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aidc-toolkit/utility",
3
- "version": "0.0.1",
3
+ "version": "0.9.1",
4
4
  "description": "Foundational utilities for AIDC Toolkit",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -20,23 +20,24 @@
20
20
  },
21
21
  "scripts": {
22
22
  "eslint": "eslint .",
23
- "build": "tsup src/index.ts --format cjs,esm --dts",
23
+ "build": "tsup src/index.ts --clean --format cjs,esm --dts",
24
+ "build-dev": "npm run build && tsc src/index.ts --outDir dist --target esnext --moduleResolution nodenext --module nodenext --emitDeclarationOnly --declaration --declarationMap",
24
25
  "test": "vitest run"
25
26
  },
26
27
  "devDependencies": {
27
- "@aidc-toolkit/dev": "^0.0.1",
28
- "@eslint/js": "^9.10.0",
29
- "@stylistic/eslint-plugin": "^2.7.2",
30
- "eslint-config-love": "^64.0.0",
31
- "eslint-plugin-jsdoc": "^50.2.2",
28
+ "@aidc-toolkit/dev": "^0.9.1",
29
+ "@eslint/js": "^9.11.1",
30
+ "@stylistic/eslint-plugin": "^2.8.0",
31
+ "eslint-config-love": "^71.0.0",
32
+ "eslint-plugin-jsdoc": "^50.3.0",
32
33
  "ts-node": "^10.9.2",
33
- "tsup": "^8.2.4",
34
- "typescript": "^5.5.4",
35
- "typescript-eslint": "^8.4.0",
36
- "vitest": "^2.0.5"
34
+ "tsup": "^8.3.0",
35
+ "typescript": "^5.6.2",
36
+ "typescript-eslint": "^8.7.0",
37
+ "vitest": "^2.1.1"
37
38
  },
38
39
  "dependencies": {
39
- "@aidc-toolkit/core": "^0.0.1",
40
- "i18next": "^23.14.0"
40
+ "@aidc-toolkit/core": "^0.9.1",
41
+ "i18next": "^23.15.1"
41
42
  }
42
43
  }
@@ -1,8 +1,7 @@
1
- import { IterationHelper, type IterationSource } from "./iteration.js";
2
1
  import i18next, { utilityNS } from "./locale/i18n.js";
3
2
  import { RegExpValidator } from "./reg_exp.js";
4
3
  import type { StringValidation, StringValidator } from "./string.js";
5
- import { type TransformationCallback, Transformer } from "./transformer.js";
4
+ import { Transformer } from "./transformer.js";
6
5
 
7
6
  /**
8
7
  * Exclusion options for validating and creating strings based on character sets.
@@ -60,8 +59,17 @@ export interface CharacterSetValidation extends StringValidation {
60
59
  /**
61
60
  * Character set validator. Validates a string against a specified character set.
62
61
  */
63
- export class CharacterSetValidator implements StringValidator {
62
+ export class CharacterSetValidator implements StringValidator<CharacterSetValidation> {
64
63
  private static readonly NOT_ALL_NUMERIC_VALIDATOR = new class extends RegExpValidator {
64
+ /**
65
+ * Create an error message for an all-numeric string.
66
+ *
67
+ * @param _s
68
+ * String.
69
+ *
70
+ * @returns
71
+ * Error message.
72
+ */
65
73
  protected override createErrorMessage(_s: string): string {
66
74
  return i18next.t("CharacterSetValidator.stringMustNotBeAllNumeric", {
67
75
  ns: utilityNS
@@ -174,7 +182,7 @@ export class CharacterSetValidator implements StringValidator {
174
182
  * Component as a string or undefined.
175
183
  */
176
184
  private static componentToString(component: string | (() => string) | undefined): string | undefined {
177
- return component === undefined || typeof component === "string" ? component : component();
185
+ return typeof component === "function" ? component() : component;
178
186
  }
179
187
 
180
188
  /**
@@ -182,8 +190,6 @@ export class CharacterSetValidator implements StringValidator {
182
190
  *
183
191
  * @param exclusion
184
192
  * Exclusion.
185
- *
186
- * @throws RangeError
187
193
  */
188
194
  protected validateExclusion(exclusion: Exclusion): void {
189
195
  if (exclusion !== Exclusion.None && !this._exclusionSupport.includes(exclusion)) {
@@ -203,8 +209,6 @@ export class CharacterSetValidator implements StringValidator {
203
209
  *
204
210
  * @param validation
205
211
  * Character set validation parameters.
206
- *
207
- * @throws RangeError
208
212
  */
209
213
  validate(s: string, validation?: CharacterSetValidation): void {
210
214
  const length = s.length;
@@ -452,8 +456,6 @@ export class CharacterSetCreator extends CharacterSetValidator {
452
456
  *
453
457
  * @returns
454
458
  * Shift required to skip all all-numeric strings.
455
- *
456
- * @throws RangeError
457
459
  */
458
460
  private allNumericShift(shiftForward: boolean, length: number, value: bigint): bigint {
459
461
  let shift: bigint;
@@ -495,10 +497,16 @@ export class CharacterSetCreator extends CharacterSetValidator {
495
497
  *
496
498
  * @param length
497
499
  * Length.
498
- *
499
- * @throws RangeError
500
500
  */
501
501
  private validateLength(length: number): void {
502
+ if (length < 0) {
503
+ throw new RangeError(i18next.t("CharacterSetValidator.lengthMustBeGreaterThanOrEqualTo", {
504
+ ns: utilityNS,
505
+ length,
506
+ minimumLength: 0
507
+ }));
508
+ }
509
+
502
510
  if (length > CharacterSetCreator.MAXIMUM_STRING_LENGTH) {
503
511
  throw new RangeError(i18next.t("CharacterSetValidator.lengthMustBeLessThanOrEqualTo", {
504
512
  ns: utilityNS,
@@ -509,31 +517,75 @@ export class CharacterSetCreator extends CharacterSetValidator {
509
517
  }
510
518
 
511
519
  /**
512
- * Do the work for the creation methods. Undocumented parameters are as defined in the public methods.
520
+ * Create a string by mapping a value to the equivalent characters in the character set across the length of the
521
+ * string.
522
+ *
523
+ * @param length
524
+ * Required string length.
513
525
  *
514
- * @template T
515
- * Type defined by creation callback to do the work of creation (string or string[]).
526
+ * @param value
527
+ * Numeric value of the string.
528
+ *
529
+ * @param exclusion
530
+ * Strings to be excluded from the range of outputs. See {@link Exclusion} for possible values and their meaning.
531
+ *
532
+ * @param tweak
533
+ * If provided, the numerical value of the string is "tweaked" using an {@link EncryptionTransformer | encryption
534
+ * transformer}.
535
+ *
536
+ * @param creationCallback
537
+ * If provided, called after the string is constructed to create the final value.
538
+ *
539
+ * @returns
540
+ * String created from the value.
541
+ */
542
+ create(length: number, value: number | bigint, exclusion?: Exclusion, tweak?: number | bigint, creationCallback?: CreationCallback): string;
543
+
544
+ /**
545
+ * Create multiple strings by mapping each value to the equivalent characters in the character set across the length
546
+ * of the string. Equivalent to calling this method for each individual value.
516
547
  *
517
548
  * @param length
518
- * See public methods.
549
+ * Required string length.
550
+ *
551
+ * @param values
552
+ * Numeric values of the strings.
519
553
  *
520
554
  * @param exclusion
521
- * See public methods.
555
+ * Strings to be excluded from the range of outputs. See {@link Exclusion} for possible values and their meaning.
522
556
  *
523
557
  * @param tweak
524
- * See public methods.
558
+ * If provided, the numerical value of the strings are "tweaked" using an {@link EncryptionTransformer | encryption
559
+ * transformer}.
525
560
  *
526
561
  * @param creationCallback
527
- * See public methods.
562
+ * If provided, called after each string is constructed to create the final value.
528
563
  *
529
- * @param createCallback
530
- * Callback to do the work of creation, whether for a single value or a sequence. Called with the appropriate
531
- * transformer and a transformation callback method to map individual transformed values to strings.
564
+ * @returns
565
+ * Iterable iterator over strings created from the values.
566
+ */
567
+ create(length: number, values: Iterable<number | bigint>, exclusion?: Exclusion, tweak?: number | bigint, creationCallback?: CreationCallback): IterableIterator<string>;
568
+
569
+ /**
570
+ * Create a string or multiple strings. This signature exists to allow similar overloaded methods in other classes
571
+ * to call this method correctly.
572
+ *
573
+ * @param length
574
+ *
575
+ * @param valueOrValues
576
+ *
577
+ * @param exclusion
578
+ *
579
+ * @param tweak
580
+ *
581
+ * @param creationCallback
532
582
  *
533
583
  * @returns
534
- * Created string or iterable iterator over created strings.
535
584
  */
536
- private doCreate<T>(length: number, exclusion: Exclusion, tweak: number | bigint | undefined, creationCallback: CreationCallback | undefined, createCallback: (transformer: Transformer, transformationCallback: TransformationCallback<string>) => T): T {
585
+ create(length: number, valueOrValues: number | bigint | Iterable<number | bigint>, exclusion?: Exclusion, tweak?: number | bigint, creationCallback?: CreationCallback): string | IterableIterator<string>;
586
+
587
+ // eslint-disable-next-line jsdoc/require-jsdoc
588
+ create(length: number, valueOrValues: number | bigint | Iterable<number | bigint>, exclusion: Exclusion = Exclusion.None, tweak?: number | bigint, creationCallback?: CreationCallback): string | IterableIterator<string> {
537
589
  this.validateLength(length);
538
590
  this.validateExclusion(exclusion);
539
591
 
@@ -543,7 +595,7 @@ export class CharacterSetCreator extends CharacterSetValidator {
543
595
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
544
596
  const transformer = Transformer.get(this._exclusionDomains[exclusion]![length], tweak);
545
597
 
546
- return createCallback(transformer, (transformedValue, index) => {
598
+ return transformer.forward(valueOrValues, (transformedValue, index) => {
547
599
  let s = "";
548
600
 
549
601
  // Empty string is valid.
@@ -575,93 +627,6 @@ export class CharacterSetCreator extends CharacterSetValidator {
575
627
  });
576
628
  }
577
629
 
578
- /**
579
- * Create a string by mapping a value to the equivalent characters in the character set across the length of the
580
- * string.
581
- *
582
- * @param length
583
- * Required string length.
584
- *
585
- * @param value
586
- * Numeric value of the string.
587
- *
588
- * @param exclusion
589
- * Strings to be excluded from the range of outputs. See {@link Exclusion} for possible values and their meaning.
590
- *
591
- * @param tweak
592
- * If provided, the numerical value of the string is "tweaked" using an {@link EncryptionTransformer | encryption
593
- * transformer}.
594
- *
595
- * @param creationCallback
596
- * If provided, called after the string is constructed to create the final value.
597
- *
598
- * @returns
599
- * String created from the value.
600
- */
601
- create(length: number, value: number | bigint, exclusion: Exclusion = Exclusion.None, tweak?: number | bigint, creationCallback?: CreationCallback): string {
602
- return this.doCreate(length, exclusion, tweak, creationCallback, (transformer, transformationCallback) => transformer.forward(BigInt(value), transformationCallback));
603
- }
604
-
605
- /**
606
- * Create a sequence of strings by mapping each value to the equivalent characters in the character set across the
607
- * length of the string. Equivalent to calling {@link create} for `value = startValue + n` where `n` ranges from `0`
608
- * to `count - 1`.
609
- *
610
- * The implementation uses {@link Transformer.forwardSequence}, so the values are created only as needed.
611
- *
612
- * @param length
613
- * See {@link create}.
614
- *
615
- * @param startValue
616
- * Numeric value of the first string. Strings are created from `startValue` to `startValue + count - 1`.
617
- *
618
- * @param count
619
- * The number of strings to create.
620
- *
621
- * @param exclusion
622
- * See {@link create}.
623
- *
624
- * @param tweak
625
- * See {@link create}.
626
- *
627
- * @param creationCallback
628
- * See {@link create}.
629
- *
630
- * @returns
631
- * Iterable iterator over created strings.
632
- */
633
- createSequence(length: number, startValue: number | bigint, count: number, exclusion: Exclusion = Exclusion.None, tweak?: number | bigint, creationCallback?: CreationCallback): IterableIterator<string> {
634
- return this.doCreate(length, exclusion, tweak, creationCallback, (transformer, transformationCallback) => transformer.forwardSequence(BigInt(startValue), count, transformationCallback));
635
- }
636
-
637
- /**
638
- * Create multiple strings by mapping each value to the equivalent characters in the character set across the length
639
- * of the string. Equivalent to calling {@link create} for each value in the values source.
640
- *
641
- * The implementation uses {@link Transformer.forwardMultiple}, so the values are created only as needed.
642
- *
643
- * @param length
644
- * See {@link create}.
645
- *
646
- * @param valuesSource
647
- * Source of values.
648
- *
649
- * @param exclusion
650
- * See {@link create}.
651
- *
652
- * @param tweak
653
- * See {@link create}.
654
- *
655
- * @param creationCallback
656
- * See {@link create}.
657
- *
658
- * @returns
659
- * Iterable iterator over created strings.
660
- */
661
- createMultiple(length: number, valuesSource: IterationSource<number | bigint>, exclusion: Exclusion = Exclusion.None, tweak?: number | bigint, creationCallback?: CreationCallback): IterableIterator<string> {
662
- return this.doCreate(length, exclusion, tweak, creationCallback, (transformer, transformationCallback) => transformer.forwardMultiple(IterationHelper.from(valuesSource).map(value => BigInt(value)), transformationCallback));
663
- }
664
-
665
630
  /**
666
631
  * Determine the value for a string.
667
632
  *
@@ -678,7 +643,7 @@ export class CharacterSetCreator extends CharacterSetValidator {
678
643
  * @returns
679
644
  * Numeric value of the string.
680
645
  */
681
- value(s: string, exclusion: Exclusion = Exclusion.None, tweak?: number | bigint): bigint {
646
+ valueFor(s: string, exclusion: Exclusion = Exclusion.None, tweak?: number | bigint): bigint {
682
647
  const length = s.length;
683
648
 
684
649
  this.validateLength(length);
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
- export * from "./iteration.js";
1
+ export * from "./sequencer.js";
2
2
  export * from "./transformer.js";
3
- export * from "./string.js";
3
+ export type * from "./string.js";
4
4
  export * from "./reg_exp.js";
5
5
  export * from "./record.js";
6
6
  export * from "./character_set.js";
@@ -2,10 +2,10 @@ export const localeStrings = {
2
2
  Transformer: {
3
3
  domainMustBeGreaterThanZero: "Domain {{domain}} must be greater than 0",
4
4
  tweakMustBeGreaterThanOrEqualToZero: "Tweak {{tweak}} must be greater than or equal to 0",
5
- valueMustBeGreaterThanOrEqualToZero: "Value {{startValue}} must be greater than or equal to 0",
6
- startValueMustBeGreaterThanOrEqualToZero: "Start value {{startValue}} must be greater than or equal to 0",
7
- valueMustBeLessThan: "Value {{endValue}} must be less than {{domain}}",
8
- endValueMustBeLessThan: "End value (start value + count - 1) {{endValue}} must be less than {{domain}}"
5
+ valueMustBeGreaterThanOrEqualToZero: "Value {{value}} must be greater than or equal to 0",
6
+ valueMustBeLessThan: "Value {{value}} must be less than {{domain}}",
7
+ minValueMustBeGreaterThanOrEqualToZero: "Minimum value {{minValue}} must be greater than or equal to 0",
8
+ maxValueMustBeLessThan: "Maximum value {{maxValue}} must be less than {{domain}}"
9
9
  },
10
10
  RegExpValidator: {
11
11
  stringDoesNotMatchPattern: "String {{s}} does not match pattern"
@@ -1,6 +1,12 @@
1
1
  import type { localeStrings } from "./en/locale_strings.js";
2
2
 
3
+ /**
4
+ * Internationalization module.
5
+ */
3
6
  declare module "i18next" {
7
+ /**
8
+ * Custom type options for this package.
9
+ */
4
10
  interface CustomTypeOptions {
5
11
  resources: {
6
12
  // Extract the type from the English locale strings object.
package/src/record.ts CHANGED
@@ -49,8 +49,6 @@ export class RecordValidator<T> implements StringValidator {
49
49
  *
50
50
  * @param key
51
51
  * Record key.
52
- *
53
- * @throws RangeError
54
52
  */
55
53
  validate(key: string): void {
56
54
  if (this.record[key] === undefined) {
package/src/reg_exp.ts CHANGED
@@ -50,6 +50,9 @@ export class RegExpValidator implements StringValidator {
50
50
  });
51
51
  }
52
52
 
53
+ /**
54
+ * @inheritDoc
55
+ */
53
56
  validate(s: string): void {
54
57
  if (!this._regExp.test(s)) {
55
58
  throw new RangeError(this.createErrorMessage(s));
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Sequencer. Defines an ascending or descending sequence of big integers implemented as an iterable iterator.
3
+ */
4
+ export class Sequencer implements Iterable<bigint>, IterableIterator<bigint> {
5
+ /**
6
+ * Start value (inclusive).
7
+ */
8
+ private readonly _startValue: bigint;
9
+
10
+ /**
11
+ * End value (exclusive).
12
+ */
13
+ private readonly _endValue: bigint;
14
+
15
+ /**
16
+ * Count of values.
17
+ */
18
+ private readonly _count: number;
19
+
20
+ /**
21
+ * Delta to the next value; equal to the sign of the count.
22
+ */
23
+ private readonly _nextDelta: 1n | -1n;
24
+
25
+ /**
26
+ * Minimum value (inclusive).
27
+ */
28
+ private readonly _minValue: bigint;
29
+
30
+ /**
31
+ * Maximum value (inclusive).
32
+ */
33
+ private readonly _maxValue: bigint;
34
+
35
+ /**
36
+ * Next value.
37
+ */
38
+ private _nextValue: bigint;
39
+
40
+ /**
41
+ * Constructor.
42
+ *
43
+ * @param startValue
44
+ * Start value.
45
+ *
46
+ * @param count
47
+ * Count of values. If count is zero or positive, iteration ascends from start value, otherwise it descends from
48
+ * start value.
49
+ */
50
+ constructor(startValue: number | bigint, count: number) {
51
+ this._startValue = BigInt(startValue);
52
+ this._endValue = this._startValue + BigInt(count);
53
+ this._count = count;
54
+
55
+ const ascending = count >= 0;
56
+
57
+ if (ascending) {
58
+ this._nextDelta = 1n;
59
+ this._minValue = this._startValue;
60
+ this._maxValue = this._endValue - 1n;
61
+ } else {
62
+ this._nextDelta = -1n;
63
+ this._minValue = this._endValue + 1n;
64
+ this._maxValue = this._startValue;
65
+ }
66
+
67
+ this._nextValue = this._startValue;
68
+ }
69
+
70
+ /**
71
+ * Get the start value (inclusive).
72
+ */
73
+ get startValue(): bigint {
74
+ return this._startValue;
75
+ }
76
+
77
+ /**
78
+ * Get the end value (exclusive).
79
+ */
80
+ get endValue(): bigint {
81
+ return this._endValue;
82
+ }
83
+
84
+ /**
85
+ * Get the count of values.
86
+ */
87
+ get count(): number {
88
+ return this._count;
89
+ }
90
+
91
+ /**
92
+ * Get the minimum value (inclusive).
93
+ */
94
+ get minValue(): bigint {
95
+ return this._minValue;
96
+ }
97
+
98
+ /**
99
+ * Get the maximum value (inclusive).
100
+ */
101
+ get maxValue(): bigint {
102
+ return this._maxValue;
103
+ }
104
+
105
+ /**
106
+ * Iterable implementation.
107
+ *
108
+ * @returns
109
+ * this
110
+ */
111
+ [Symbol.iterator](): this {
112
+ return this;
113
+ }
114
+
115
+ /**
116
+ * Iterator implementation.
117
+ *
118
+ * @returns
119
+ * Iterator result. If iterator is exhausted, the value is absolute value of the count.
120
+ */
121
+ next(): IteratorResult<bigint, number> {
122
+ const done = this._nextValue === this._endValue;
123
+
124
+ let result: IteratorResult<bigint, number>;
125
+
126
+ if (!done) {
127
+ result = {
128
+ value: this._nextValue
129
+ };
130
+
131
+ this._nextValue += this._nextDelta;
132
+ } else {
133
+ result = {
134
+ done: true,
135
+ value: Math.abs(this._count)
136
+ };
137
+ }
138
+
139
+ return result;
140
+ }
141
+
142
+ /**
143
+ * Reset the iterator.
144
+ */
145
+ reset(): void {
146
+ // Reset simply returns to the start.
147
+ this._nextValue = this._startValue;
148
+ }
149
+ }
package/src/string.ts CHANGED
@@ -8,7 +8,7 @@ export interface StringValidation {
8
8
  /**
9
9
  * String validator interface.
10
10
  */
11
- export interface StringValidator {
11
+ export interface StringValidator<V extends StringValidation = StringValidation> {
12
12
  /**
13
13
  * Validate a string and throw an exception if validation fails.
14
14
  *
@@ -18,5 +18,5 @@ export interface StringValidator {
18
18
  * @param validation
19
19
  * String validation parameters.
20
20
  */
21
- validate: (s: string, validation?: StringValidation) => void;
21
+ validate: (s: string, validation?: V) => void;
22
22
  }