ts-fortress 8.5.2 → 9.0.0
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.
- package/README.md +8 -5
- package/dist/predefined/brand/string/email.d.mts +2 -2
- package/dist/predefined/brand/string/email.d.mts.map +1 -1
- package/dist/predefined/brand/string/email.mjs +4 -3
- package/dist/predefined/brand/string/email.mjs.map +1 -1
- package/dist/predefined/brand/string/iso-8601.d.mts +2 -2
- package/dist/predefined/brand/string/iso-8601.d.mts.map +1 -1
- package/dist/predefined/brand/string/iso-8601.mjs +2 -3
- package/dist/predefined/brand/string/iso-8601.mjs.map +1 -1
- package/dist/predefined/brand/string/json-string.d.mts +2 -2
- package/dist/predefined/brand/string/json-string.d.mts.map +1 -1
- package/dist/predefined/brand/string/json-string.mjs +4 -3
- package/dist/predefined/brand/string/json-string.mjs.map +1 -1
- package/dist/predefined/brand/string/uri.d.mts +2 -2
- package/dist/predefined/brand/string/uri.d.mts.map +1 -1
- package/dist/predefined/brand/string/uri.mjs +2 -3
- package/dist/predefined/brand/string/uri.mjs.map +1 -1
- package/dist/predefined/brand/string/uuid.d.mts +5 -5
- package/dist/predefined/brand/string/uuid.d.mts.map +1 -1
- package/dist/predefined/brand/string/uuid.mjs +2 -2
- package/dist/predefined/brand/string/uuid.mjs.map +1 -1
- package/dist/primitives/string.d.mts +37 -22
- package/dist/primitives/string.d.mts.map +1 -1
- package/dist/primitives/string.mjs +24 -2
- package/dist/primitives/string.mjs.map +1 -1
- package/package.json +3 -3
- package/src/predefined/brand/string/email.mts +9 -7
- package/src/predefined/brand/string/iso-8601.mts +11 -8
- package/src/predefined/brand/string/json-string.mts +9 -8
- package/src/predefined/brand/string/non-empty-string.test.mts +4 -3
- package/src/predefined/brand/string/uri.mts +7 -7
- package/src/predefined/brand/string/uuid.mts +14 -10
- package/src/primitives/string.mts +125 -41
- package/src/primitives/string.test.mts +95 -11
- package/src/record/key-value-record.test.mts +1 -2
package/README.md
CHANGED
|
@@ -277,17 +277,20 @@ Slug.is('feature-beta'); // true
|
|
|
277
277
|
|
|
278
278
|
Slug.is('Feature-Flag'); // false (fails regex)
|
|
279
279
|
|
|
280
|
-
type SlugType = t.TypeOf<typeof Slug>;
|
|
280
|
+
type SlugType = t.TypeOf<typeof Slug>;
|
|
281
|
+
// inferred as a `feature${string}` template literal branded with
|
|
282
|
+
// `NonEmptyString`, `MinLengthString<6>` and `MaxLengthString<32>`
|
|
281
283
|
```
|
|
282
284
|
|
|
283
285
|
String constraints:
|
|
284
286
|
|
|
285
|
-
- `startsWith`, `endsWith`, `includes`
|
|
286
|
-
- `lowercase`, `uppercase
|
|
287
|
-
- `
|
|
287
|
+
- `startsWith`, `endsWith`, `includes` — narrow the result to the corresponding template literal type
|
|
288
|
+
- `lowercase`, `uppercase`
|
|
289
|
+
- `nonempty` — brands the result as `NonEmptyString` (from [ts-type-forge](https://github.com/noshiro-pf/ts-type-forge))
|
|
290
|
+
- `minLength`, `maxLength` — brand the result as `MinLengthString<N>` / `MaxLengthString<N>` (from ts-type-forge), which preserve the natural subtyping between length bounds
|
|
288
291
|
- `regex`
|
|
289
292
|
|
|
290
|
-
|
|
293
|
+
`minLength` / `maxLength` accept plain `number`s, which must be positive integers (`>= 1`) — passing a non-integer or a value less than `1` throws a `TypeError` when the type is constructed. Number literals in the range `1..39` are encoded in the result brand (`MinLengthString<N>` / `MaxLengthString<N>`); larger literals and dynamically computed `number`s are still enforced at runtime, but the result type falls back to plain `string` to keep the type-level computation cheap.
|
|
291
294
|
|
|
292
295
|
### Number constraints
|
|
293
296
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type Brand } from 'ts-type-forge';
|
|
1
|
+
import { type Brand, type NonEmptyString } from 'ts-type-forge';
|
|
2
2
|
import { type Type } from '../../../type.mjs';
|
|
3
|
-
export type Email = Brand<
|
|
3
|
+
export type Email = Brand<NonEmptyString, 'Email'>;
|
|
4
4
|
/**
|
|
5
5
|
* @link https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address
|
|
6
6
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.d.mts","sourceRoot":"","sources":["../../../../src/predefined/brand/string/email.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"email.d.mts","sourceRoot":"","sources":["../../../../src/predefined/brand/string/email.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAGhE,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAI9C,MAAM,MAAM,KAAK,GAAG,KAAK,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;AAEnD;;GAEG;AACH,eAAO,MAAM,KAAK,GAChB,UAAU,OAAO,CACf,QAAQ,CAAC;IACP,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC,CACH,KACA,IAAI,CAAC,KAAK,CAQT,CAAC"}
|
|
@@ -8,10 +8,11 @@ import { string } from '../../../primitives/string.mjs';
|
|
|
8
8
|
* @link https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address
|
|
9
9
|
*/
|
|
10
10
|
const email = (options) => brand({
|
|
11
|
-
baseType: string(options?.defaultValue ?? defaultEmail
|
|
11
|
+
baseType: string(options?.defaultValue ?? defaultEmail, {
|
|
12
|
+
nonempty: true,
|
|
13
|
+
}),
|
|
12
14
|
is: (s) => regexpEmailAddress.test(s),
|
|
13
|
-
|
|
14
|
-
brandKeys: ['Email', 'NonEmptyString'],
|
|
15
|
+
brandKeys: ['Email'],
|
|
15
16
|
typeName: 'Email',
|
|
16
17
|
});
|
|
17
18
|
const defaultEmail = 'ts-f.o.r.t.r.e.s.s@gmail.com';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.mjs","sources":["../../../../src/predefined/brand/string/email.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"email.mjs","sources":["../../../../src/predefined/brand/string/email.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;AASA;;AAEG;AACI,MAAM,KAAK,GAAG,CACnB,OAIC,KAED,KAAK,CAAqC;IACxC,QAAQ,EAAE,MAAM,CAAC,OAAO,EAAE,YAAY,IAAI,YAAY,EAAE;AACtD,QAAA,QAAQ,EAAE,IAAI;KACf,CAAC;IACF,EAAE,EAAE,CAAC,CAAC,KAAiB,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;IACjD,SAAS,EAAE,CAAC,OAAO,CAAC;AACpB,IAAA,QAAQ,EAAE,OAAO;AAClB,CAAA;AAEH,MAAM,YAAY,GAAG,8BAA8B;AAEnD,MAAM,kBAAkB;AACtB;AACA,uIAAuI;;;;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type Brand } from 'ts-type-forge';
|
|
1
|
+
import { type Brand, type NonEmptyString } from 'ts-type-forge';
|
|
2
2
|
import { type Type, type TypeOf } from '../../../type.mjs';
|
|
3
|
-
export type Iso8601 = Brand<
|
|
3
|
+
export type Iso8601 = Brand<NonEmptyString, 'Iso8601'>;
|
|
4
4
|
/**
|
|
5
5
|
* @link https://github.com/validatorjs/validator.js/tree/v13.1.17?tab=readme-ov-file#validators
|
|
6
6
|
* @link https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"iso-8601.d.mts","sourceRoot":"","sources":["../../../../src/predefined/brand/string/iso-8601.mts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"iso-8601.d.mts","sourceRoot":"","sources":["../../../../src/predefined/brand/string/iso-8601.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,KAAK,EACV,KAAK,cAAc,EAEpB,MAAM,eAAe,CAAC;AAIvB,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAI3D,MAAM,MAAM,OAAO,GAAG,KAAK,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;AAEvD;;;;GAIG;AACH,eAAO,MAAM,OAAO,GAAI,UAAU,sBAAsB,KAAG,IAAI,CAAC,OAAO,CAWtE,CAAC;AAEF,QAAA,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgB1B,CAAC;AAEH,KAAK,sBAAsB,GAAG,MAAM,CAAC,OAAO,sBAAsB,CAAC,CAAC"}
|
|
@@ -15,10 +15,9 @@ const iso8601 = (options) => {
|
|
|
15
15
|
const filledOptions = ISO8601ValidatorOption.fill(options);
|
|
16
16
|
const is = isISO8601(filledOptions);
|
|
17
17
|
return brand({
|
|
18
|
-
baseType: string(filledOptions.defaultValue),
|
|
18
|
+
baseType: string(filledOptions.defaultValue, { nonempty: true }),
|
|
19
19
|
is,
|
|
20
|
-
|
|
21
|
-
brandKeys: ['Iso8601', 'NonEmptyString'],
|
|
20
|
+
brandKeys: ['Iso8601'],
|
|
22
21
|
typeName: 'Iso8601',
|
|
23
22
|
});
|
|
24
23
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"iso-8601.mjs","sources":["../../../../src/predefined/brand/string/iso-8601.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"iso-8601.mjs","sources":["../../../../src/predefined/brand/string/iso-8601.mts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type Brand } from 'ts-type-forge';
|
|
1
|
+
import { type Brand, type NonEmptyString } from 'ts-type-forge';
|
|
2
2
|
import { type Type } from '../../../type.mjs';
|
|
3
|
-
export type JsonString = Brand<
|
|
3
|
+
export type JsonString = Brand<NonEmptyString, 'JsonString'>;
|
|
4
4
|
/**
|
|
5
5
|
* @link https://github.com/validatorjs/validator.js/tree/v13.1.17?tab=readme-ov-file#validators
|
|
6
6
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-string.d.mts","sourceRoot":"","sources":["../../../../src/predefined/brand/string/json-string.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"json-string.d.mts","sourceRoot":"","sources":["../../../../src/predefined/brand/string/json-string.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAGhE,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAI9C,MAAM,MAAM,UAAU,GAAG,KAAK,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;AAE7D;;GAEG;AACH,eAAO,MAAM,UAAU,GACrB,UAAU,OAAO,CACf,QAAQ,CAAC;IACP,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC,CACH,KACA,IAAI,CAAC,UAAU,CAQd,CAAC"}
|
|
@@ -8,10 +8,11 @@ import { string } from '../../../primitives/string.mjs';
|
|
|
8
8
|
* @link https://github.com/validatorjs/validator.js/tree/v13.1.17?tab=readme-ov-file#validators
|
|
9
9
|
*/
|
|
10
10
|
const jsonString = (options) => brand({
|
|
11
|
-
baseType: string(options?.defaultValue ?? defaultJsonString
|
|
11
|
+
baseType: string(options?.defaultValue ?? defaultJsonString, {
|
|
12
|
+
nonempty: true,
|
|
13
|
+
}),
|
|
12
14
|
is: isJsonString,
|
|
13
|
-
|
|
14
|
-
brandKeys: ['JsonString', 'NonEmptyString'],
|
|
15
|
+
brandKeys: ['JsonString'],
|
|
15
16
|
typeName: 'JsonString',
|
|
16
17
|
});
|
|
17
18
|
const defaultJsonString = '{}';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-string.mjs","sources":["../../../../src/predefined/brand/string/json-string.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;AAUA;;AAEG;AACI,MAAM,UAAU,GAAG,CACxB,OAIC,KAED,KAAK,
|
|
1
|
+
{"version":3,"file":"json-string.mjs","sources":["../../../../src/predefined/brand/string/json-string.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;AAUA;;AAEG;AACI,MAAM,UAAU,GAAG,CACxB,OAIC,KAED,KAAK,CAA0C;IAC7C,QAAQ,EAAE,MAAM,CAAC,OAAO,EAAE,YAAY,IAAI,iBAAiB,EAAE;AAC3D,QAAA,QAAQ,EAAE,IAAI;KACf,CAAC;AACF,IAAA,EAAE,EAAE,YAAY;IAChB,SAAS,EAAE,CAAC,YAAY,CAAC;AACzB,IAAA,QAAQ,EAAE,YAAY;AACvB,CAAA;AAEH,MAAM,iBAAiB,GAAG,IAAI;AAE9B,MAAM,YAAY,GAAG,CAAC,GAAW,KAAuB;AACtD,IAAA,IAAI;QACF,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzC;AAAE,IAAA,MAAM;AACN,QAAA,OAAO,KAAK;IACd;AACF,CAAC;;;;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type Brand } from 'ts-type-forge';
|
|
1
|
+
import { type Brand, type NonEmptyString } from 'ts-type-forge';
|
|
2
2
|
import { type Type } from '../../../type.mjs';
|
|
3
|
-
export type Uri = Brand<
|
|
3
|
+
export type Uri = Brand<NonEmptyString, 'Uri'>;
|
|
4
4
|
/**
|
|
5
5
|
* @link https://url.spec.whatwg.org/
|
|
6
6
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uri.d.mts","sourceRoot":"","sources":["../../../../src/predefined/brand/string/uri.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"uri.d.mts","sourceRoot":"","sources":["../../../../src/predefined/brand/string/uri.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAGhE,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAI9C,MAAM,MAAM,GAAG,GAAG,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;AAE/C;;GAEG;AACH,eAAO,MAAM,GAAG,GACd,UAAU,OAAO,CACf,QAAQ,CAAC;IACP,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC,CACH,KACA,IAAI,CAAC,GAAG,CAMP,CAAC"}
|
|
@@ -8,10 +8,9 @@ import { string } from '../../../primitives/string.mjs';
|
|
|
8
8
|
* @link https://url.spec.whatwg.org/
|
|
9
9
|
*/
|
|
10
10
|
const uri = (options) => brand({
|
|
11
|
-
baseType: string(options?.defaultValue ?? defaultUri),
|
|
11
|
+
baseType: string(options?.defaultValue ?? defaultUri, { nonempty: true }),
|
|
12
12
|
is: isUri,
|
|
13
|
-
|
|
14
|
-
brandKeys: ['Uri', 'NonEmptyString'],
|
|
13
|
+
brandKeys: ['Uri'],
|
|
15
14
|
typeName: 'Uri',
|
|
16
15
|
});
|
|
17
16
|
const defaultUri = 'https://example.com';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uri.mjs","sources":["../../../../src/predefined/brand/string/uri.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"uri.mjs","sources":["../../../../src/predefined/brand/string/uri.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;AASA;;AAEG;AACI,MAAM,GAAG,GAAG,CACjB,OAIC,KAED,KAAK,CAAmC;AACtC,IAAA,QAAQ,EAAE,MAAM,CAAC,OAAO,EAAE,YAAY,IAAI,UAAU,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AACzE,IAAA,EAAE,EAAE,KAAK;IACT,SAAS,EAAE,CAAC,KAAK,CAAC;AAClB,IAAA,QAAQ,EAAE,KAAK;AAChB,CAAA;AAEH,MAAM,UAAU,GAAG,qBAAqB;AAExC;AACA;AACA,MAAM,KAAK,GAAG,CAAC,CAAS,KAAe,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;;;;"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { type Brand, type TypeEq, type UintRangeInclusive } from 'ts-type-forge';
|
|
1
|
+
import { type Brand, type NonEmptyString, type TypeEq, type UintRangeInclusive } from 'ts-type-forge';
|
|
2
2
|
import { type Type } from '../../../type.mjs';
|
|
3
3
|
export type UuidVersion = UintRangeInclusive<1, 8>;
|
|
4
4
|
export type UuidBaseString<V extends UuidVersion = UuidVersion> = `${string}-${string}-${V}${string}-${string}-${string}` | '00000000-0000-0000-0000-000000000000' | 'ffffffff-ffff-ffff-ffff-ffffffffffff';
|
|
5
|
-
export type Uuid = Brand<UuidBaseString, 'Uuid'
|
|
6
|
-
export type Uuid4 = Brand<UuidBaseString<4
|
|
7
|
-
export type Uuid6 = Brand<UuidBaseString<6
|
|
8
|
-
export type Uuid7 = Brand<UuidBaseString<7
|
|
5
|
+
export type Uuid = Brand<UuidBaseString & NonEmptyString, 'Uuid'>;
|
|
6
|
+
export type Uuid4 = Brand<UuidBaseString<4> & NonEmptyString, 'Uuid'>;
|
|
7
|
+
export type Uuid6 = Brand<UuidBaseString<6> & NonEmptyString, 'Uuid'>;
|
|
8
|
+
export type Uuid7 = Brand<UuidBaseString<7> & NonEmptyString, 'Uuid'>;
|
|
9
9
|
/**
|
|
10
10
|
* @link https://github.com/validatorjs/validator.js/tree/v13.1.17?tab=readme-ov-file#validators
|
|
11
11
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uuid.d.mts","sourceRoot":"","sources":["../../../../src/predefined/brand/string/uuid.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,KAAK,
|
|
1
|
+
{"version":3,"file":"uuid.d.mts","sourceRoot":"","sources":["../../../../src/predefined/brand/string/uuid.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,KAAK,EACV,KAAK,cAAc,EAEnB,KAAK,MAAM,EACX,KAAK,kBAAkB,EACxB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,MAAM,WAAW,GAAG,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAEnD,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,IAC1D,GAAG,MAAM,IAAI,MAAM,IAAI,CAAC,GAAG,MAAM,IAAI,MAAM,IAAI,MAAM,EAAE,GACvD,sCAAsC,GACtC,sCAAsC,CAAC;AAK3C,MAAM,MAAM,IAAI,GAAG,KAAK,CAAC,cAAc,GAAG,cAAc,EAAE,MAAM,CAAC,CAAC;AAElE,MAAM,MAAM,KAAK,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,cAAc,EAAE,MAAM,CAAC,CAAC;AAEtE,MAAM,MAAM,KAAK,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,cAAc,EAAE,MAAM,CAAC,CAAC;AAEtE,MAAM,MAAM,KAAK,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,cAAc,EAAE,MAAM,CAAC,CAAC;AAEtE;;GAEG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,SAAS,WAAW,GAAG,2BAA2B,EACtE,UAAU,mBAAmB,CAAC,CAAC,CAAC,KAC/B,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CA2BhB,CAAC;AAEF,KAAK,2BAA2B,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,OAAO,CAAC;AAEnE,KAAK,MAAM,CAAC,CAAC,SAAS,WAAW,GAAG,2BAA2B,IAC7D,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,IAAI,GACrB,KAAK,GACL,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,IAAI,GACvB,KAAK,GACL,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,IAAI,GACvB,KAAK,GACL,IAAI,CAAC;AAEf,eAAO,MAAM,MAAM,GAAI,eAAe,MAAM,KAAG,IAAI,CAAC,KAAK,CAIrD,CAAC;AAEL,eAAO,MAAM,MAAM,GAAI,eAAe,MAAM,KAAG,IAAI,CAAC,KAAK,CAIrD,CAAC;AAEL,eAAO,MAAM,MAAM,GAAI,eAAe,MAAM,KAAG,IAAI,CAAC,KAAK,CAIrD,CAAC;AAEL,KAAK,mBAAmB,CAAC,CAAC,SAAS,WAAW,GAAG,2BAA2B,IAC1E,OAAO,CACL,QAAQ,CAAC;IACP;;OAEG;IACH,OAAO,EAAE,CAAC,CAAC;IAEX,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC,CACH,CAAC"}
|
|
@@ -13,11 +13,11 @@ const uuid = (options) => {
|
|
|
13
13
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
14
14
|
return brand({
|
|
15
15
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
16
|
-
baseType: string(defaultValue),
|
|
16
|
+
baseType: string(defaultValue, { nonempty: true }),
|
|
17
17
|
is: (s) => uuidDef[version](s),
|
|
18
18
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
19
19
|
defaultValue: defaultValue,
|
|
20
|
-
brandKeys: ['Uuid'
|
|
20
|
+
brandKeys: ['Uuid'],
|
|
21
21
|
typeName: options?.typeName ??
|
|
22
22
|
(version === 4
|
|
23
23
|
? 'UuidV4'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uuid.mjs","sources":["../../../../src/predefined/brand/string/uuid.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"uuid.mjs","sources":["../../../../src/predefined/brand/string/uuid.mts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type NonEmptyString } from 'ts-type-forge';
|
|
1
|
+
import { type MaxLengthString, type MinLengthString, type NonEmptyString, type SmallInt } from 'ts-type-forge';
|
|
2
2
|
import { type Type } from '../type.mjs';
|
|
3
3
|
export declare function string(defaultValue?: string): Type<string>;
|
|
4
|
-
export declare function string<S extends string, const C extends
|
|
5
|
-
type
|
|
4
|
+
export declare function string<S extends string, const C extends RawConstraints>(defaultValue: S & DefaultValueType<S, C>, constraints: C): Type<ConstraintsResultType<C>>;
|
|
5
|
+
type RawConstraints = Partial<Readonly<{
|
|
6
6
|
nonempty: true;
|
|
7
7
|
minLength: number;
|
|
8
8
|
maxLength: number;
|
|
@@ -16,49 +16,64 @@ type Constraints = Partial<Readonly<{
|
|
|
16
16
|
/**
|
|
17
17
|
* The set of constraints accepted by {@link string}.
|
|
18
18
|
*/
|
|
19
|
-
export type StringTypeConstraints =
|
|
19
|
+
export type StringTypeConstraints = RawConstraints;
|
|
20
20
|
/**
|
|
21
21
|
* The result type produced by {@link string} for a given set of constraints
|
|
22
|
-
* `C` (e.g. `nonempty` yields {@link NonEmptyString}, `
|
|
23
|
-
*
|
|
22
|
+
* `C` (e.g. `nonempty` yields {@link NonEmptyString}, `minLength` /
|
|
23
|
+
* `maxLength` yield {@link MinLengthString} / {@link MaxLengthString},
|
|
24
|
+
* `startsWith` narrows to a template literal type, etc.).
|
|
24
25
|
*/
|
|
25
26
|
export type StringConstraintsResultType<C extends StringTypeConstraints> = ConstraintsResultType<C>;
|
|
26
|
-
type DefaultValueType<S extends string, C extends
|
|
27
|
-
type ConstraintsResultType<C extends
|
|
28
|
-
type DefaultValueWhenStartsWithIsOn<C extends
|
|
27
|
+
type DefaultValueType<S extends string, C extends RawConstraints> = DefaultValueWhenNonemptyIsOn<S, C> & DefaultValueWhenMinLengthIsOn<S, C> & DefaultValueWhenMaxLengthIsOn<S, C> & DefaultValueWhenStartsWithIsOn<C> & DefaultValueWhenEndsWithIsOn<C> & DefaultValueWhenIncludesIsOn<C> & DefaultValueWhenUppercaseIsOn<S, C> & DefaultValueWhenLowercaseIsOn<S, C>;
|
|
28
|
+
type ConstraintsResultType<C extends RawConstraints> = ConstraintsResultTypeWhenNonemptyIsOn<C> & ConstraintsResultTypeWhenMinLengthIsOn<C> & ConstraintsResultTypeWhenMaxLengthIsOn<C> & DefaultValueWhenStartsWithIsOn<C> & DefaultValueWhenEndsWithIsOn<C> & DefaultValueWhenIncludesIsOn<C>;
|
|
29
|
+
type DefaultValueWhenStartsWithIsOn<C extends RawConstraints> = C extends Readonly<{
|
|
29
30
|
startsWith: infer S extends string;
|
|
30
31
|
}> ? `${S}${string}` : string;
|
|
31
|
-
type DefaultValueWhenEndsWithIsOn<C extends
|
|
32
|
+
type DefaultValueWhenEndsWithIsOn<C extends RawConstraints> = C extends Readonly<{
|
|
32
33
|
endsWith: infer E extends string;
|
|
33
34
|
}> ? `${string}${E}` : string;
|
|
34
|
-
type DefaultValueWhenIncludesIsOn<C extends
|
|
35
|
+
type DefaultValueWhenIncludesIsOn<C extends RawConstraints> = C extends Readonly<{
|
|
35
36
|
includes: infer M extends string;
|
|
36
37
|
}> ? `${string}${M}${string}` : string;
|
|
37
|
-
type DefaultValueWhenUppercaseIsOn<S extends string, C extends
|
|
38
|
+
type DefaultValueWhenUppercaseIsOn<S extends string, C extends RawConstraints> = C extends Readonly<{
|
|
38
39
|
uppercase: true;
|
|
39
40
|
}> ? CastUppercase<S> : string;
|
|
40
|
-
type DefaultValueWhenLowercaseIsOn<S extends string, C extends
|
|
41
|
+
type DefaultValueWhenLowercaseIsOn<S extends string, C extends RawConstraints> = C extends Readonly<{
|
|
41
42
|
lowercase: true;
|
|
42
43
|
}> ? CastLowercase<S> : string;
|
|
43
44
|
type CastLowercase<S extends string> = S extends Lowercase<S> ? S : never;
|
|
44
45
|
type CastUppercase<S extends string> = S extends Uppercase<S> ? S : never;
|
|
45
|
-
type DefaultValueWhenNonemptyIsOn<S extends string, C extends
|
|
46
|
+
type DefaultValueWhenNonemptyIsOn<S extends string, C extends RawConstraints> = C extends Readonly<{
|
|
46
47
|
nonempty: true;
|
|
47
48
|
}> ? RejectEmptyString<S> : string;
|
|
48
49
|
type RejectEmptyString<S extends string> = S extends '' ? never : S;
|
|
49
|
-
type ConstraintsResultTypeWhenNonemptyIsOn<C extends
|
|
50
|
+
type ConstraintsResultTypeWhenNonemptyIsOn<C extends RawConstraints> = C extends Readonly<{
|
|
50
51
|
nonempty: true;
|
|
51
52
|
}> ? NonEmptyString : string;
|
|
52
|
-
|
|
53
|
+
/**
|
|
54
|
+
* The length-bound literals supported at the type level: `SmallInt<'>0'>`
|
|
55
|
+
* (i.e. `1 | 2 | ... | 39`). Only bounds within this range are encoded in
|
|
56
|
+
* the result brand; larger literals and non-literal `number`s collapse to
|
|
57
|
+
* plain `string` to keep the type-level computation cheap (the runtime
|
|
58
|
+
* constraint applies regardless). Non-integer bounds and bounds less than 1
|
|
59
|
+
* throw a `TypeError` when the type is constructed.
|
|
60
|
+
*/
|
|
61
|
+
type SupportedLengthLiteral = SmallInt<'>0'>;
|
|
62
|
+
type ConstraintsResultTypeWhenMinLengthIsOn<C extends RawConstraints> = C extends Readonly<{
|
|
63
|
+
minLength: infer M extends number;
|
|
64
|
+
}> ? [M] extends [SupportedLengthLiteral] ? MinLengthString<M> : string : string;
|
|
65
|
+
type ConstraintsResultTypeWhenMaxLengthIsOn<C extends RawConstraints> = C extends Readonly<{
|
|
66
|
+
maxLength: infer M extends number;
|
|
67
|
+
}> ? [M] extends [SupportedLengthLiteral] ? MaxLengthString<M> : string : string;
|
|
68
|
+
type DefaultValueWhenMinLengthIsOn<S extends string, C extends RawConstraints> = C extends Readonly<{
|
|
53
69
|
minLength: infer M extends number;
|
|
54
|
-
}> ? StringWithMinLength<S, M> : string;
|
|
55
|
-
type DefaultValueWhenMaxLengthIsOn<S extends string, C extends
|
|
70
|
+
}> ? [M] extends [SupportedLengthLiteral] ? StringWithMinLength<S, M> : string : string;
|
|
71
|
+
type DefaultValueWhenMaxLengthIsOn<S extends string, C extends RawConstraints> = C extends Readonly<{
|
|
56
72
|
maxLength: infer M extends number;
|
|
57
|
-
}> ? StringWithMaxLength<S, M> : string;
|
|
73
|
+
}> ? [M] extends [SupportedLengthLiteral] ? StringWithMaxLength<S, M> : string : string;
|
|
58
74
|
type StringWithMinLength<S extends string, N extends number> = HasLengthAtLeast<S, N> extends true ? S : never;
|
|
59
75
|
type StringWithMaxLength<S extends string, N extends number> = HasLengthAtMost<S, N> extends true ? S : never;
|
|
60
|
-
type HasLengthAtLeast<S extends string, N extends number, Acc extends readonly unknown[] = readonly []> = string extends S ? true :
|
|
61
|
-
type HasLengthAtMost<S extends string, N extends number, Acc extends readonly unknown[] = readonly []> = string extends S ? true :
|
|
62
|
-
type IsNegative<N extends number> = `${N}` extends `-${string}` ? true : false;
|
|
76
|
+
type HasLengthAtLeast<S extends string, N extends number, Acc extends readonly unknown[] = readonly []> = string extends S ? true : Acc['length'] extends N ? true : S extends '' ? false : S extends `${infer _}${infer Rest}` ? HasLengthAtLeast<Rest, N, readonly [...Acc, unknown]> : false;
|
|
77
|
+
type HasLengthAtMost<S extends string, N extends number, Acc extends readonly unknown[] = readonly []> = string extends S ? true : S extends '' ? true : Acc['length'] extends N ? false : S extends `${infer _}${infer Rest}` ? HasLengthAtMost<Rest, N, readonly [...Acc, unknown]> : false;
|
|
63
78
|
export {};
|
|
64
79
|
//# sourceMappingURL=string.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"string.d.mts","sourceRoot":"","sources":["../../src/primitives/string.mts"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"string.d.mts","sourceRoot":"","sources":["../../src/primitives/string.mts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isRegExp } from '@sindresorhus/is';
|
|
2
|
-
import { isString, Arr, Result } from 'ts-data-forge';
|
|
2
|
+
import { isString, Arr, Result, PositiveInt } from 'ts-data-forge';
|
|
3
3
|
import { createPrimitiveType } from '../utils/create-primitive-type.mjs';
|
|
4
4
|
import { refine } from '../other-types/refine.mjs';
|
|
5
5
|
import '../other-types/unknown.mjs';
|
|
@@ -14,7 +14,11 @@ function string(defaultValue = '', constraints) {
|
|
|
14
14
|
Arr.isArrayOfLength(Object.keys(constraints), 0)) {
|
|
15
15
|
return baseType;
|
|
16
16
|
}
|
|
17
|
-
const
|
|
17
|
+
const constraintsResult = validateConstraints(constraints);
|
|
18
|
+
if (Result.isErr(constraintsResult)) {
|
|
19
|
+
throw new TypeError(constraintsResult.value);
|
|
20
|
+
}
|
|
21
|
+
const constraintsPredicate = createConstraintsPredicate(constraintsResult.value);
|
|
18
22
|
const defaultValueConstraintsCheck = constraintsPredicate(defaultValue);
|
|
19
23
|
if (Result.isErr(defaultValueConstraintsCheck)) {
|
|
20
24
|
throw new Error(defaultValueConstraintsCheck.value);
|
|
@@ -26,6 +30,24 @@ function string(defaultValue = '', constraints) {
|
|
|
26
30
|
typeName: 'string',
|
|
27
31
|
});
|
|
28
32
|
}
|
|
33
|
+
const validateConstraints = (constraints) => {
|
|
34
|
+
const result1 = validateLengthConstraint('minLength', constraints.minLength);
|
|
35
|
+
if (Result.isErr(result1)) {
|
|
36
|
+
return Result.err(result1.value);
|
|
37
|
+
}
|
|
38
|
+
const result2 = validateLengthConstraint('maxLength', constraints.maxLength);
|
|
39
|
+
if (Result.isErr(result2)) {
|
|
40
|
+
return Result.err(result2.value);
|
|
41
|
+
}
|
|
42
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
43
|
+
return Result.ok(constraints);
|
|
44
|
+
};
|
|
45
|
+
const validateLengthConstraint = (constraintName, constraintValue) => {
|
|
46
|
+
if (constraintValue !== undefined && !PositiveInt.is(constraintValue)) {
|
|
47
|
+
return Result.err(`${constraintName} constraint for string must be a positive integer, but ${constraintValue} was passed.`);
|
|
48
|
+
}
|
|
49
|
+
return Result.ok(true);
|
|
50
|
+
};
|
|
29
51
|
const createConstraintsPredicate = (constraints) => (value) => {
|
|
30
52
|
const { nonempty, minLength, maxLength, startsWith, endsWith, includes, uppercase, lowercase, regex, ..._rest } = constraints;
|
|
31
53
|
if (nonempty === true && value.length === 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"string.mjs","sources":["../../src/primitives/string.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"string.mjs","sources":["../../src/primitives/string.mts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-fortress",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript"
|
|
@@ -76,8 +76,8 @@
|
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
78
|
"@sindresorhus/is": "^8.1.0",
|
|
79
|
-
"ts-data-forge": "^
|
|
80
|
-
"ts-type-forge": "^
|
|
79
|
+
"ts-data-forge": "^9.0.0",
|
|
80
|
+
"ts-type-forge": "^5.0.0"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
83
|
"@rollup/plugin-replace": "6.0.3",
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { type Brand } from 'ts-type-forge';
|
|
1
|
+
import { type Brand, type NonEmptyString } from 'ts-type-forge';
|
|
2
2
|
import { brand } from '../../../brand/index.mjs';
|
|
3
3
|
import { string } from '../../../primitives/index.mjs';
|
|
4
4
|
import { type Type } from '../../../type.mjs';
|
|
5
5
|
|
|
6
|
-
// A valid e-mail address is always non-empty, so
|
|
7
|
-
|
|
6
|
+
// A valid e-mail address is always non-empty, so `Email` is branded on top of
|
|
7
|
+
// `NonEmptyString` and is assignable to it.
|
|
8
|
+
export type Email = Brand<NonEmptyString, 'Email'>;
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* @link https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address
|
|
@@ -16,11 +17,12 @@ export const email = (
|
|
|
16
17
|
}>
|
|
17
18
|
>,
|
|
18
19
|
): Type<Email> =>
|
|
19
|
-
brand({
|
|
20
|
-
baseType: string(options?.defaultValue ?? defaultEmail
|
|
20
|
+
brand<NonEmptyString, readonly ['Email']>({
|
|
21
|
+
baseType: string(options?.defaultValue ?? defaultEmail, {
|
|
22
|
+
nonempty: true,
|
|
23
|
+
}),
|
|
21
24
|
is: (s): s is Email => regexpEmailAddress.test(s),
|
|
22
|
-
|
|
23
|
-
brandKeys: ['Email', 'NonEmptyString'],
|
|
25
|
+
brandKeys: ['Email'],
|
|
24
26
|
typeName: 'Email',
|
|
25
27
|
});
|
|
26
28
|
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type Brand,
|
|
3
|
+
type NonEmptyString,
|
|
4
|
+
type StrictOmit,
|
|
5
|
+
} from 'ts-type-forge';
|
|
2
6
|
import { brand } from '../../../brand/index.mjs';
|
|
3
7
|
import { boolean, string } from '../../../primitives/index.mjs';
|
|
4
8
|
import { record } from '../../../record/index.mjs';
|
|
5
9
|
import { type Type, type TypeOf } from '../../../type.mjs';
|
|
6
10
|
|
|
7
|
-
// A valid ISO 8601 string always begins with a 4-digit year, so
|
|
8
|
-
//
|
|
9
|
-
export type Iso8601 = Brand<
|
|
11
|
+
// A valid ISO 8601 string always begins with a 4-digit year, so `Iso8601` is
|
|
12
|
+
// branded on top of `NonEmptyString` and is assignable to it.
|
|
13
|
+
export type Iso8601 = Brand<NonEmptyString, 'Iso8601'>;
|
|
10
14
|
|
|
11
15
|
/**
|
|
12
16
|
* @link https://github.com/validatorjs/validator.js/tree/v13.1.17?tab=readme-ov-file#validators
|
|
@@ -18,11 +22,10 @@ export const iso8601 = (options?: ISO8601ValidatorOption): Type<Iso8601> => {
|
|
|
18
22
|
|
|
19
23
|
const is = isISO8601(filledOptions);
|
|
20
24
|
|
|
21
|
-
return brand({
|
|
22
|
-
baseType: string(filledOptions.defaultValue),
|
|
25
|
+
return brand<NonEmptyString, readonly ['Iso8601']>({
|
|
26
|
+
baseType: string(filledOptions.defaultValue, { nonempty: true }),
|
|
23
27
|
is,
|
|
24
|
-
|
|
25
|
-
brandKeys: ['Iso8601', 'NonEmptyString'],
|
|
28
|
+
brandKeys: ['Iso8601'],
|
|
26
29
|
typeName: 'Iso8601',
|
|
27
30
|
});
|
|
28
31
|
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { isNonNullObject } from 'ts-data-forge';
|
|
2
|
-
import { type Brand } from 'ts-type-forge';
|
|
2
|
+
import { type Brand, type NonEmptyString } from 'ts-type-forge';
|
|
3
3
|
import { brand } from '../../../brand/index.mjs';
|
|
4
4
|
import { string } from '../../../primitives/index.mjs';
|
|
5
5
|
import { type Type } from '../../../type.mjs';
|
|
6
6
|
|
|
7
|
-
// A valid JSON string (an object or array literal) is always non-empty, so
|
|
8
|
-
//
|
|
9
|
-
export type JsonString = Brand<
|
|
7
|
+
// A valid JSON string (an object or array literal) is always non-empty, so
|
|
8
|
+
// `JsonString` is branded on top of `NonEmptyString` and is assignable to it.
|
|
9
|
+
export type JsonString = Brand<NonEmptyString, 'JsonString'>;
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* @link https://github.com/validatorjs/validator.js/tree/v13.1.17?tab=readme-ov-file#validators
|
|
@@ -18,11 +18,12 @@ export const jsonString = (
|
|
|
18
18
|
}>
|
|
19
19
|
>,
|
|
20
20
|
): Type<JsonString> =>
|
|
21
|
-
brand({
|
|
22
|
-
baseType: string(options?.defaultValue ?? defaultJsonString
|
|
21
|
+
brand<NonEmptyString, readonly ['JsonString']>({
|
|
22
|
+
baseType: string(options?.defaultValue ?? defaultJsonString, {
|
|
23
|
+
nonempty: true,
|
|
24
|
+
}),
|
|
23
25
|
is: isJsonString,
|
|
24
|
-
|
|
25
|
-
brandKeys: ['JsonString', 'NonEmptyString'],
|
|
26
|
+
brandKeys: ['JsonString'],
|
|
26
27
|
typeName: 'JsonString',
|
|
27
28
|
});
|
|
28
29
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { expectType } from 'ts-data-forge';
|
|
2
|
-
import { type NonEmptyString } from 'ts-type-forge';
|
|
2
|
+
import { type MinLengthString, type NonEmptyString } from 'ts-type-forge';
|
|
3
3
|
import { type TypeOf } from '../../../type.mjs';
|
|
4
4
|
import { type Email } from './email.mjs';
|
|
5
5
|
import { type Iso8601 } from './iso-8601.mjs';
|
|
@@ -57,8 +57,9 @@ describe('nonEmptyString with extra constraints', () => {
|
|
|
57
57
|
test('forwards constraints to string and still enforces non-emptiness', () => {
|
|
58
58
|
const t = nonEmptyString('abcd', { minLength: 3 });
|
|
59
59
|
|
|
60
|
-
//
|
|
61
|
-
|
|
60
|
+
// Length constraints (e.g. `minLength`) refine the result type with the
|
|
61
|
+
// corresponding length-constrained brand.
|
|
62
|
+
expectType<TypeOf<typeof t>, NonEmptyString & MinLengthString<3>>('=');
|
|
62
63
|
|
|
63
64
|
assert.isTrue(t.is('abcd'));
|
|
64
65
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { type Brand } from 'ts-type-forge';
|
|
1
|
+
import { type Brand, type NonEmptyString } from 'ts-type-forge';
|
|
2
2
|
import { brand } from '../../../brand/index.mjs';
|
|
3
3
|
import { string } from '../../../primitives/index.mjs';
|
|
4
4
|
import { type Type } from '../../../type.mjs';
|
|
5
5
|
|
|
6
|
-
// An absolute URI/URL always has a non-empty scheme, so
|
|
7
|
-
|
|
6
|
+
// An absolute URI/URL always has a non-empty scheme, so `Uri` is branded on
|
|
7
|
+
// top of `NonEmptyString` and is assignable to it.
|
|
8
|
+
export type Uri = Brand<NonEmptyString, 'Uri'>;
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* @link https://url.spec.whatwg.org/
|
|
@@ -16,11 +17,10 @@ export const uri = (
|
|
|
16
17
|
}>
|
|
17
18
|
>,
|
|
18
19
|
): Type<Uri> =>
|
|
19
|
-
brand({
|
|
20
|
-
baseType: string(options?.defaultValue ?? defaultUri),
|
|
20
|
+
brand<NonEmptyString, readonly ['Uri']>({
|
|
21
|
+
baseType: string(options?.defaultValue ?? defaultUri, { nonempty: true }),
|
|
21
22
|
is: isUri,
|
|
22
|
-
|
|
23
|
-
brandKeys: ['Uri', 'NonEmptyString'],
|
|
23
|
+
brandKeys: ['Uri'],
|
|
24
24
|
typeName: 'Uri',
|
|
25
25
|
});
|
|
26
26
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type Brand,
|
|
3
|
+
type NonEmptyString,
|
|
3
4
|
type ReadonlyRecord,
|
|
4
5
|
type TypeEq,
|
|
5
6
|
type UintRangeInclusive,
|
|
@@ -16,14 +17,15 @@ export type UuidBaseString<V extends UuidVersion = UuidVersion> =
|
|
|
16
17
|
| 'ffffffff-ffff-ffff-ffff-ffffffffffff';
|
|
17
18
|
|
|
18
19
|
// A UUID always has the dash-separated hexadecimal form, so it is never an
|
|
19
|
-
// empty string
|
|
20
|
-
|
|
20
|
+
// empty string; `Uuid` is branded on top of `NonEmptyString` and is
|
|
21
|
+
// assignable to it.
|
|
22
|
+
export type Uuid = Brand<UuidBaseString & NonEmptyString, 'Uuid'>;
|
|
21
23
|
|
|
22
|
-
export type Uuid4 = Brand<UuidBaseString<4
|
|
24
|
+
export type Uuid4 = Brand<UuidBaseString<4> & NonEmptyString, 'Uuid'>;
|
|
23
25
|
|
|
24
|
-
export type Uuid6 = Brand<UuidBaseString<6
|
|
26
|
+
export type Uuid6 = Brand<UuidBaseString<6> & NonEmptyString, 'Uuid'>;
|
|
25
27
|
|
|
26
|
-
export type Uuid7 = Brand<UuidBaseString<7
|
|
28
|
+
export type Uuid7 = Brand<UuidBaseString<7> & NonEmptyString, 'Uuid'>;
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
31
|
* @link https://github.com/validatorjs/validator.js/tree/v13.1.17?tab=readme-ov-file#validators
|
|
@@ -31,20 +33,22 @@ export type Uuid7 = Brand<UuidBaseString<7>, 'Uuid' | 'NonEmptyString'>;
|
|
|
31
33
|
export const uuid = <V extends UuidVersion | UuidVersionAdditionalOption>(
|
|
32
34
|
options?: UuidValidatorOption<V>,
|
|
33
35
|
): Type<UuidOf<V>> => {
|
|
34
|
-
type T = Brand<UuidBaseString, 'Uuid'
|
|
36
|
+
type T = Brand<UuidBaseString & NonEmptyString, 'Uuid'>;
|
|
35
37
|
|
|
36
38
|
const defaultValue = options?.defaultValue ?? nilUuid;
|
|
37
39
|
|
|
38
40
|
const version = options?.version ?? 'all';
|
|
39
41
|
|
|
40
42
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
41
|
-
return brand<UuidBaseString, readonly ['Uuid'
|
|
43
|
+
return brand<UuidBaseString & NonEmptyString, readonly ['Uuid']>({
|
|
42
44
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
43
|
-
baseType: string(defaultValue) as Type<
|
|
45
|
+
baseType: string(defaultValue, { nonempty: true }) as Type<
|
|
46
|
+
UuidBaseString & NonEmptyString
|
|
47
|
+
>,
|
|
44
48
|
is: (s): s is T => uuidDef[version](s),
|
|
45
49
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
46
|
-
defaultValue: defaultValue as UuidBaseString,
|
|
47
|
-
brandKeys: ['Uuid'
|
|
50
|
+
defaultValue: defaultValue as UuidBaseString & NonEmptyString,
|
|
51
|
+
brandKeys: ['Uuid'],
|
|
48
52
|
typeName:
|
|
49
53
|
options?.typeName ??
|
|
50
54
|
(version === 4
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import { isRegExp } from '@sindresorhus/is';
|
|
2
|
-
import { Arr, expectType, isString, Result } from 'ts-data-forge';
|
|
3
|
-
import {
|
|
2
|
+
import { Arr, expectType, isString, PositiveInt, Result } from 'ts-data-forge';
|
|
3
|
+
import {
|
|
4
|
+
type MaxLengthString,
|
|
5
|
+
type MinLengthString,
|
|
6
|
+
type NonEmptyString,
|
|
7
|
+
type SmallInt,
|
|
8
|
+
} from 'ts-type-forge';
|
|
4
9
|
import { refine } from '../other-types/index.mjs';
|
|
5
10
|
import { type Type } from '../type.mjs';
|
|
6
11
|
import { createPrimitiveType } from '../utils/index.mjs';
|
|
7
12
|
|
|
8
13
|
export function string(defaultValue?: string): Type<string>;
|
|
9
14
|
|
|
10
|
-
export function string<S extends string, const C extends
|
|
15
|
+
export function string<S extends string, const C extends RawConstraints>(
|
|
11
16
|
defaultValue: S & DefaultValueType<S, C>,
|
|
12
17
|
constraints: C,
|
|
13
18
|
): Type<ConstraintsResultType<C>>;
|
|
14
19
|
|
|
15
|
-
export function string<C extends
|
|
20
|
+
export function string<C extends RawConstraints>(
|
|
16
21
|
defaultValue: string = '',
|
|
17
22
|
constraints?: C,
|
|
18
23
|
): Type<string> {
|
|
@@ -29,7 +34,15 @@ export function string<C extends Constraints>(
|
|
|
29
34
|
return baseType;
|
|
30
35
|
}
|
|
31
36
|
|
|
32
|
-
const
|
|
37
|
+
const constraintsResult = validateConstraints(constraints);
|
|
38
|
+
|
|
39
|
+
if (Result.isErr(constraintsResult)) {
|
|
40
|
+
throw new TypeError(constraintsResult.value);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const constraintsPredicate = createConstraintsPredicate(
|
|
44
|
+
constraintsResult.value,
|
|
45
|
+
);
|
|
33
46
|
|
|
34
47
|
const defaultValueConstraintsCheck = constraintsPredicate(defaultValue);
|
|
35
48
|
|
|
@@ -45,7 +58,7 @@ export function string<C extends Constraints>(
|
|
|
45
58
|
});
|
|
46
59
|
}
|
|
47
60
|
|
|
48
|
-
type
|
|
61
|
+
type RawConstraints = Partial<
|
|
49
62
|
Readonly<{
|
|
50
63
|
nonempty: true;
|
|
51
64
|
minLength: number;
|
|
@@ -59,22 +72,37 @@ type Constraints = Partial<
|
|
|
59
72
|
}>
|
|
60
73
|
>;
|
|
61
74
|
|
|
75
|
+
type Constraints = Partial<
|
|
76
|
+
Readonly<{
|
|
77
|
+
nonempty: true;
|
|
78
|
+
minLength: PositiveInt;
|
|
79
|
+
maxLength: PositiveInt;
|
|
80
|
+
startsWith: string;
|
|
81
|
+
endsWith: string;
|
|
82
|
+
includes: string;
|
|
83
|
+
uppercase: true;
|
|
84
|
+
lowercase: true;
|
|
85
|
+
regex: RegExp;
|
|
86
|
+
}>
|
|
87
|
+
>;
|
|
88
|
+
|
|
62
89
|
/**
|
|
63
90
|
* The set of constraints accepted by {@link string}.
|
|
64
91
|
*/
|
|
65
|
-
export type StringTypeConstraints =
|
|
92
|
+
export type StringTypeConstraints = RawConstraints;
|
|
66
93
|
|
|
67
94
|
/**
|
|
68
95
|
* The result type produced by {@link string} for a given set of constraints
|
|
69
|
-
* `C` (e.g. `nonempty` yields {@link NonEmptyString}, `
|
|
70
|
-
*
|
|
96
|
+
* `C` (e.g. `nonempty` yields {@link NonEmptyString}, `minLength` /
|
|
97
|
+
* `maxLength` yield {@link MinLengthString} / {@link MaxLengthString},
|
|
98
|
+
* `startsWith` narrows to a template literal type, etc.).
|
|
71
99
|
*/
|
|
72
100
|
export type StringConstraintsResultType<C extends StringTypeConstraints> =
|
|
73
101
|
ConstraintsResultType<C>;
|
|
74
102
|
|
|
75
103
|
type DefaultValueType<
|
|
76
104
|
S extends string,
|
|
77
|
-
C extends
|
|
105
|
+
C extends RawConstraints,
|
|
78
106
|
> = DefaultValueWhenNonemptyIsOn<S, C> &
|
|
79
107
|
DefaultValueWhenMinLengthIsOn<S, C> &
|
|
80
108
|
DefaultValueWhenMaxLengthIsOn<S, C> &
|
|
@@ -84,59 +112,89 @@ type DefaultValueType<
|
|
|
84
112
|
DefaultValueWhenUppercaseIsOn<S, C> &
|
|
85
113
|
DefaultValueWhenLowercaseIsOn<S, C>;
|
|
86
114
|
|
|
87
|
-
type ConstraintsResultType<C extends
|
|
115
|
+
type ConstraintsResultType<C extends RawConstraints> =
|
|
88
116
|
ConstraintsResultTypeWhenNonemptyIsOn<C> &
|
|
117
|
+
ConstraintsResultTypeWhenMinLengthIsOn<C> &
|
|
118
|
+
ConstraintsResultTypeWhenMaxLengthIsOn<C> &
|
|
89
119
|
DefaultValueWhenStartsWithIsOn<C> &
|
|
90
120
|
DefaultValueWhenEndsWithIsOn<C> &
|
|
91
121
|
DefaultValueWhenIncludesIsOn<C>;
|
|
92
122
|
|
|
93
|
-
type DefaultValueWhenStartsWithIsOn<C extends
|
|
123
|
+
type DefaultValueWhenStartsWithIsOn<C extends RawConstraints> =
|
|
94
124
|
C extends Readonly<{
|
|
95
125
|
startsWith: infer S extends string;
|
|
96
126
|
}>
|
|
97
127
|
? `${S}${string}`
|
|
98
128
|
: string;
|
|
99
129
|
|
|
100
|
-
type DefaultValueWhenEndsWithIsOn<C extends
|
|
130
|
+
type DefaultValueWhenEndsWithIsOn<C extends RawConstraints> =
|
|
101
131
|
C extends Readonly<{
|
|
102
132
|
endsWith: infer E extends string;
|
|
103
133
|
}>
|
|
104
134
|
? `${string}${E}`
|
|
105
135
|
: string;
|
|
106
136
|
|
|
107
|
-
type DefaultValueWhenIncludesIsOn<C extends
|
|
137
|
+
type DefaultValueWhenIncludesIsOn<C extends RawConstraints> =
|
|
108
138
|
C extends Readonly<{
|
|
109
139
|
includes: infer M extends string;
|
|
110
140
|
}>
|
|
111
141
|
? `${string}${M}${string}`
|
|
112
142
|
: string;
|
|
113
143
|
|
|
114
|
-
type DefaultValueWhenUppercaseIsOn<S extends string, C extends
|
|
144
|
+
type DefaultValueWhenUppercaseIsOn<S extends string, C extends RawConstraints> =
|
|
115
145
|
C extends Readonly<{ uppercase: true }> ? CastUppercase<S> : string;
|
|
116
146
|
|
|
117
|
-
type DefaultValueWhenLowercaseIsOn<S extends string, C extends
|
|
147
|
+
type DefaultValueWhenLowercaseIsOn<S extends string, C extends RawConstraints> =
|
|
118
148
|
C extends Readonly<{ lowercase: true }> ? CastLowercase<S> : string;
|
|
119
149
|
|
|
120
150
|
type CastLowercase<S extends string> = S extends Lowercase<S> ? S : never;
|
|
121
151
|
|
|
122
152
|
type CastUppercase<S extends string> = S extends Uppercase<S> ? S : never;
|
|
123
153
|
|
|
124
|
-
type DefaultValueWhenNonemptyIsOn<S extends string, C extends
|
|
154
|
+
type DefaultValueWhenNonemptyIsOn<S extends string, C extends RawConstraints> =
|
|
125
155
|
C extends Readonly<{ nonempty: true }> ? RejectEmptyString<S> : string;
|
|
126
156
|
|
|
127
157
|
type RejectEmptyString<S extends string> = S extends '' ? never : S;
|
|
128
158
|
|
|
129
|
-
type ConstraintsResultTypeWhenNonemptyIsOn<C extends
|
|
159
|
+
type ConstraintsResultTypeWhenNonemptyIsOn<C extends RawConstraints> =
|
|
130
160
|
C extends Readonly<{ nonempty: true }> ? NonEmptyString : string;
|
|
131
161
|
|
|
132
|
-
|
|
162
|
+
/**
|
|
163
|
+
* The length-bound literals supported at the type level: `SmallInt<'>0'>`
|
|
164
|
+
* (i.e. `1 | 2 | ... | 39`). Only bounds within this range are encoded in
|
|
165
|
+
* the result brand; larger literals and non-literal `number`s collapse to
|
|
166
|
+
* plain `string` to keep the type-level computation cheap (the runtime
|
|
167
|
+
* constraint applies regardless). Non-integer bounds and bounds less than 1
|
|
168
|
+
* throw a `TypeError` when the type is constructed.
|
|
169
|
+
*/
|
|
170
|
+
type SupportedLengthLiteral = SmallInt<'>0'>;
|
|
171
|
+
|
|
172
|
+
type ConstraintsResultTypeWhenMinLengthIsOn<C extends RawConstraints> =
|
|
133
173
|
C extends Readonly<{ minLength: infer M extends number }>
|
|
134
|
-
?
|
|
174
|
+
? [M] extends [SupportedLengthLiteral]
|
|
175
|
+
? MinLengthString<M>
|
|
176
|
+
: string // minLength outside the supported literal range is not encoded in the brand
|
|
135
177
|
: string;
|
|
136
178
|
|
|
137
|
-
type
|
|
179
|
+
type ConstraintsResultTypeWhenMaxLengthIsOn<C extends RawConstraints> =
|
|
138
180
|
C extends Readonly<{ maxLength: infer M extends number }>
|
|
139
|
-
?
|
|
181
|
+
? [M] extends [SupportedLengthLiteral]
|
|
182
|
+
? MaxLengthString<M>
|
|
183
|
+
: string // maxLength outside the supported literal range is not encoded in the brand
|
|
184
|
+
: string;
|
|
185
|
+
|
|
186
|
+
type DefaultValueWhenMinLengthIsOn<S extends string, C extends RawConstraints> =
|
|
187
|
+
C extends Readonly<{ minLength: infer M extends number }>
|
|
188
|
+
? [M] extends [SupportedLengthLiteral]
|
|
189
|
+
? StringWithMinLength<S, M>
|
|
190
|
+
: string // the default's length is only checked at runtime for such bounds
|
|
191
|
+
: string;
|
|
192
|
+
|
|
193
|
+
type DefaultValueWhenMaxLengthIsOn<S extends string, C extends RawConstraints> =
|
|
194
|
+
C extends Readonly<{ maxLength: infer M extends number }>
|
|
195
|
+
? [M] extends [SupportedLengthLiteral]
|
|
196
|
+
? StringWithMaxLength<S, M>
|
|
197
|
+
: string // the default's length is only checked at runtime for such bounds
|
|
140
198
|
: string;
|
|
141
199
|
|
|
142
200
|
type StringWithMinLength<S extends string, N extends number> =
|
|
@@ -151,15 +209,13 @@ type HasLengthAtLeast<
|
|
|
151
209
|
Acc extends readonly unknown[] = readonly [],
|
|
152
210
|
> = string extends S
|
|
153
211
|
? true
|
|
154
|
-
:
|
|
212
|
+
: Acc['length'] extends N
|
|
155
213
|
? true
|
|
156
|
-
:
|
|
157
|
-
?
|
|
158
|
-
: S extends
|
|
159
|
-
?
|
|
160
|
-
:
|
|
161
|
-
? HasLengthAtLeast<Rest, N, readonly [...Acc, unknown]>
|
|
162
|
-
: false;
|
|
214
|
+
: S extends ''
|
|
215
|
+
? false
|
|
216
|
+
: S extends `${infer _}${infer Rest}`
|
|
217
|
+
? HasLengthAtLeast<Rest, N, readonly [...Acc, unknown]>
|
|
218
|
+
: false;
|
|
163
219
|
|
|
164
220
|
type HasLengthAtMost<
|
|
165
221
|
S extends string,
|
|
@@ -167,17 +223,45 @@ type HasLengthAtMost<
|
|
|
167
223
|
Acc extends readonly unknown[] = readonly [],
|
|
168
224
|
> = string extends S
|
|
169
225
|
? true
|
|
170
|
-
:
|
|
171
|
-
?
|
|
172
|
-
:
|
|
173
|
-
?
|
|
174
|
-
:
|
|
175
|
-
?
|
|
176
|
-
:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
226
|
+
: S extends ''
|
|
227
|
+
? true
|
|
228
|
+
: Acc['length'] extends N
|
|
229
|
+
? false
|
|
230
|
+
: S extends `${infer _}${infer Rest}`
|
|
231
|
+
? HasLengthAtMost<Rest, N, readonly [...Acc, unknown]>
|
|
232
|
+
: false;
|
|
233
|
+
|
|
234
|
+
const validateConstraints = (
|
|
235
|
+
constraints: RawConstraints,
|
|
236
|
+
): Result<Constraints, string> => {
|
|
237
|
+
const result1 = validateLengthConstraint('minLength', constraints.minLength);
|
|
238
|
+
|
|
239
|
+
if (Result.isErr(result1)) {
|
|
240
|
+
return Result.err(result1.value);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const result2 = validateLengthConstraint('maxLength', constraints.maxLength);
|
|
244
|
+
|
|
245
|
+
if (Result.isErr(result2)) {
|
|
246
|
+
return Result.err(result2.value);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
250
|
+
return Result.ok(constraints as Constraints);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const validateLengthConstraint = (
|
|
254
|
+
constraintName: 'maxLength' | 'minLength',
|
|
255
|
+
constraintValue: number | undefined,
|
|
256
|
+
): Result<true, string> => {
|
|
257
|
+
if (constraintValue !== undefined && !PositiveInt.is(constraintValue)) {
|
|
258
|
+
return Result.err(
|
|
259
|
+
`${constraintName} constraint for string must be a positive integer, but ${constraintValue} was passed.`,
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return Result.ok(true);
|
|
264
|
+
};
|
|
181
265
|
|
|
182
266
|
const createConstraintsPredicate =
|
|
183
267
|
(constraints: Constraints) =>
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
/* cSpell:disable */
|
|
2
2
|
|
|
3
3
|
import { expectType, Result } from 'ts-data-forge';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
type BoundedLengthString,
|
|
6
|
+
type MaxLengthString,
|
|
7
|
+
type MinLengthString,
|
|
8
|
+
type NonEmptyString,
|
|
9
|
+
} from 'ts-type-forge';
|
|
5
10
|
import { type Type, type TypeOf } from '../type.mjs';
|
|
6
11
|
import { string } from './string.mjs';
|
|
7
12
|
|
|
@@ -380,7 +385,7 @@ describe('string with constraints', () => {
|
|
|
380
385
|
test('accepts valid default value', () => {
|
|
381
386
|
const type = string('minimum', { minLength: 3 });
|
|
382
387
|
|
|
383
|
-
expectType<typeof type, Type<
|
|
388
|
+
expectType<typeof type, Type<MinLengthString<3>>>('=');
|
|
384
389
|
|
|
385
390
|
assert.isTrue(type.is('hello'));
|
|
386
391
|
|
|
@@ -396,14 +401,20 @@ describe('string with constraints', () => {
|
|
|
396
401
|
);
|
|
397
402
|
});
|
|
398
403
|
|
|
399
|
-
test('
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
404
|
+
test('throws for a minLength less than 1', () => {
|
|
405
|
+
expect(() => string('minimum', { minLength: -1 })).toThrow(
|
|
406
|
+
/^minLength constraint for string must be a positive integer, but -1 was passed\.$/u,
|
|
407
|
+
);
|
|
403
408
|
|
|
404
|
-
|
|
409
|
+
expect(() => string('minimum', { minLength: 0 })).toThrow(
|
|
410
|
+
/^minLength constraint for string must be a positive integer, but 0 was passed\.$/u,
|
|
411
|
+
);
|
|
412
|
+
});
|
|
405
413
|
|
|
406
|
-
|
|
414
|
+
test('throws for a non-integer minLength', () => {
|
|
415
|
+
expect(() => string('minimum', { minLength: 1.5 })).toThrow(
|
|
416
|
+
/^minLength constraint for string must be a positive integer, but 1\.5 was passed\.$/u,
|
|
417
|
+
);
|
|
407
418
|
});
|
|
408
419
|
});
|
|
409
420
|
|
|
@@ -411,7 +422,7 @@ describe('string with constraints', () => {
|
|
|
411
422
|
test('accepts valid default value', () => {
|
|
412
423
|
const type = string('short', { maxLength: 5 });
|
|
413
424
|
|
|
414
|
-
expectType<typeof type, Type<
|
|
425
|
+
expectType<typeof type, Type<MaxLengthString<5>>>('=');
|
|
415
426
|
|
|
416
427
|
assert.isTrue(type.is('tiny'));
|
|
417
428
|
|
|
@@ -428,6 +439,76 @@ describe('string with constraints', () => {
|
|
|
428
439
|
});
|
|
429
440
|
});
|
|
430
441
|
|
|
442
|
+
describe('minLength/maxLength outside the supported literal range', () => {
|
|
443
|
+
// Only the number literals `1..39` (`SmallInt<'>0'>`) are encoded in the
|
|
444
|
+
// result brand. A larger literal or a non-literal `number` collapses the
|
|
445
|
+
// result type to plain `string` to keep the type-level computation
|
|
446
|
+
// cheap, while the runtime constraint applies regardless. Non-integer
|
|
447
|
+
// bounds and bounds less than 1 throw at construction time.
|
|
448
|
+
|
|
449
|
+
test('the largest supported literal (39) is encoded in the brand', () => {
|
|
450
|
+
const type = string('x'.repeat(39), { minLength: 39 });
|
|
451
|
+
|
|
452
|
+
expectType<typeof type, Type<MinLengthString<39>>>('=');
|
|
453
|
+
|
|
454
|
+
assert.isTrue(type.is('x'.repeat(39)));
|
|
455
|
+
|
|
456
|
+
assert.isFalse(type.is('x'.repeat(38)));
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
test('a minLength literal outside the range (>= 40) falls back to plain string', () => {
|
|
460
|
+
const type = string('x'.repeat(40), { minLength: 40 });
|
|
461
|
+
|
|
462
|
+
expectType<typeof type, Type<string>>('=');
|
|
463
|
+
|
|
464
|
+
// The runtime constraint still applies.
|
|
465
|
+
assert.isTrue(type.is('x'.repeat(40)));
|
|
466
|
+
|
|
467
|
+
assert.isFalse(type.is('x'.repeat(39)));
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
test('a maxLength literal outside the range (>= 40) falls back to plain string', () => {
|
|
471
|
+
const type = string('', { maxLength: 100 });
|
|
472
|
+
|
|
473
|
+
expectType<typeof type, Type<string>>('=');
|
|
474
|
+
|
|
475
|
+
assert.isTrue(type.is('x'.repeat(100)));
|
|
476
|
+
|
|
477
|
+
assert.isFalse(type.is('x'.repeat(101)));
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
test('throws for a maxLength less than 1 or a non-integer maxLength', () => {
|
|
481
|
+
expect(() => string('', { maxLength: 0 })).toThrow(
|
|
482
|
+
/^maxLength constraint for string must be a positive integer, but 0 was passed\.$/u,
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
expect(() => string('', { maxLength: 2.5 })).toThrow(
|
|
486
|
+
/^maxLength constraint for string must be a positive integer, but 2\.5 was passed\.$/u,
|
|
487
|
+
);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
test('a non-literal bound falls back to plain string', () => {
|
|
491
|
+
const getBound = (): number => 5;
|
|
492
|
+
|
|
493
|
+
const type = string('', { maxLength: getBound() });
|
|
494
|
+
|
|
495
|
+
expectType<typeof type, Type<string>>('=');
|
|
496
|
+
|
|
497
|
+
assert.isTrue(type.is('12345'));
|
|
498
|
+
|
|
499
|
+
assert.isFalse(type.is('123456'));
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
test('the default value is only length-checked at runtime for an out-of-range bound', () => {
|
|
503
|
+
// With an in-range literal bound, a too-short default is already a
|
|
504
|
+
// compile error; with an out-of-range bound it compiles and the check
|
|
505
|
+
// is deferred to runtime.
|
|
506
|
+
expect(() => string('hi', { minLength: 100 })).toThrow(
|
|
507
|
+
/^defaultValue "hi" for string does not satisfy the constraint minLength = 100$/u,
|
|
508
|
+
);
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
|
|
431
512
|
describe('string constrained by regex', () => {
|
|
432
513
|
test('accepts valid default value', () => {
|
|
433
514
|
const numeric = /^\d+$/u;
|
|
@@ -556,7 +637,10 @@ describe('string with constraints', () => {
|
|
|
556
637
|
maxLength: 8,
|
|
557
638
|
});
|
|
558
639
|
|
|
559
|
-
expectType<
|
|
640
|
+
expectType<
|
|
641
|
+
typeof type,
|
|
642
|
+
Type<NonEmptyString & BoundedLengthString<3, 8>>
|
|
643
|
+
>('=');
|
|
560
644
|
|
|
561
645
|
assert.isTrue(type.is('value'));
|
|
562
646
|
|
|
@@ -635,7 +719,7 @@ describe('string with constraints', () => {
|
|
|
635
719
|
regex: digits,
|
|
636
720
|
});
|
|
637
721
|
|
|
638
|
-
expectType<typeof type, Type<
|
|
722
|
+
expectType<typeof type, Type<BoundedLengthString<4, 6>>>('=');
|
|
639
723
|
|
|
640
724
|
assert.isTrue(type.is('6789'));
|
|
641
725
|
|
|
@@ -197,8 +197,7 @@ describe(keyValueRecord, () => {
|
|
|
197
197
|
|
|
198
198
|
assert.isFalse(strNumRecord.is('string'));
|
|
199
199
|
|
|
200
|
-
|
|
201
|
-
assert.isTrue(strNumRecord.is([]));
|
|
200
|
+
assert.isFalse(strNumRecord.is([]));
|
|
202
201
|
});
|
|
203
202
|
|
|
204
203
|
test('rejects record with invalid key types', () => {
|