ts-fortress 1.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/LICENSE +201 -0
- package/README.md +324 -0
- package/dist/array/array-of-length.d.mts +6 -0
- package/dist/array/array-of-length.d.mts.map +1 -0
- package/dist/array/array-of-length.mjs +50 -0
- package/dist/array/array-of-length.mjs.map +1 -0
- package/dist/array/array.d.mts +6 -0
- package/dist/array/array.d.mts.map +1 -0
- package/dist/array/array.mjs +39 -0
- package/dist/array/array.mjs.map +1 -0
- package/dist/array/index.d.mts +5 -0
- package/dist/array/index.d.mts.map +1 -0
- package/dist/array/index.mjs +5 -0
- package/dist/array/index.mjs.map +1 -0
- package/dist/array/non-empty-array.d.mts +6 -0
- package/dist/array/non-empty-array.d.mts.map +1 -0
- package/dist/array/non-empty-array.mjs +46 -0
- package/dist/array/non-empty-array.mjs.map +1 -0
- package/dist/array/tuple.d.mts +9 -0
- package/dist/array/tuple.d.mts.map +1 -0
- package/dist/array/tuple.mjs +47 -0
- package/dist/array/tuple.mjs.map +1 -0
- package/dist/branded/brand.d.mts +11 -0
- package/dist/branded/brand.d.mts.map +1 -0
- package/dist/branded/brand.mjs +29 -0
- package/dist/branded/brand.mjs.map +1 -0
- package/dist/branded/index.d.mts +5 -0
- package/dist/branded/index.d.mts.map +1 -0
- package/dist/branded/index.mjs +20 -0
- package/dist/branded/index.mjs.map +1 -0
- package/dist/branded/number/finite-number.d.mts +3 -0
- package/dist/branded/number/finite-number.d.mts.map +1 -0
- package/dist/branded/number/finite-number.mjs +14 -0
- package/dist/branded/number/finite-number.mjs.map +1 -0
- package/dist/branded/number/index.d.mts +17 -0
- package/dist/branded/number/index.d.mts.map +1 -0
- package/dist/branded/number/index.mjs +17 -0
- package/dist/branded/number/index.mjs.map +1 -0
- package/dist/branded/number/int.d.mts +3 -0
- package/dist/branded/number/int.d.mts.map +1 -0
- package/dist/branded/number/int.mjs +14 -0
- package/dist/branded/number/int.mjs.map +1 -0
- package/dist/branded/number/int16.d.mts +3 -0
- package/dist/branded/number/int16.d.mts.map +1 -0
- package/dist/branded/number/int16.mjs +26 -0
- package/dist/branded/number/int16.mjs.map +1 -0
- package/dist/branded/number/int32.d.mts +3 -0
- package/dist/branded/number/int32.d.mts.map +1 -0
- package/dist/branded/number/int32.mjs +22 -0
- package/dist/branded/number/int32.mjs.map +1 -0
- package/dist/branded/number/non-negative-finite-number.d.mts +3 -0
- package/dist/branded/number/non-negative-finite-number.d.mts.map +1 -0
- package/dist/branded/number/non-negative-finite-number.mjs +14 -0
- package/dist/branded/number/non-negative-finite-number.mjs.map +1 -0
- package/dist/branded/number/non-zero-finite-number.d.mts +3 -0
- package/dist/branded/number/non-zero-finite-number.d.mts.map +1 -0
- package/dist/branded/number/non-zero-finite-number.mjs +14 -0
- package/dist/branded/number/non-zero-finite-number.mjs.map +1 -0
- package/dist/branded/number/non-zero-int.d.mts +3 -0
- package/dist/branded/number/non-zero-int.d.mts.map +1 -0
- package/dist/branded/number/non-zero-int.mjs +14 -0
- package/dist/branded/number/non-zero-int.mjs.map +1 -0
- package/dist/branded/number/non-zero-safe-int.d.mts +3 -0
- package/dist/branded/number/non-zero-safe-int.d.mts.map +1 -0
- package/dist/branded/number/non-zero-safe-int.mjs +14 -0
- package/dist/branded/number/non-zero-safe-int.mjs.map +1 -0
- package/dist/branded/number/positive-finite-number.d.mts +3 -0
- package/dist/branded/number/positive-finite-number.d.mts.map +1 -0
- package/dist/branded/number/positive-finite-number.mjs +22 -0
- package/dist/branded/number/positive-finite-number.mjs.map +1 -0
- package/dist/branded/number/positive-int.d.mts +3 -0
- package/dist/branded/number/positive-int.d.mts.map +1 -0
- package/dist/branded/number/positive-int.mjs +23 -0
- package/dist/branded/number/positive-int.mjs.map +1 -0
- package/dist/branded/number/positive-safe-int.d.mts +3 -0
- package/dist/branded/number/positive-safe-int.d.mts.map +1 -0
- package/dist/branded/number/positive-safe-int.mjs +24 -0
- package/dist/branded/number/positive-safe-int.mjs.map +1 -0
- package/dist/branded/number/safe-int.d.mts +3 -0
- package/dist/branded/number/safe-int.d.mts.map +1 -0
- package/dist/branded/number/safe-int.mjs +14 -0
- package/dist/branded/number/safe-int.mjs.map +1 -0
- package/dist/branded/number/safe-uint.d.mts +3 -0
- package/dist/branded/number/safe-uint.d.mts.map +1 -0
- package/dist/branded/number/safe-uint.mjs +23 -0
- package/dist/branded/number/safe-uint.mjs.map +1 -0
- package/dist/branded/number/uint.d.mts +3 -0
- package/dist/branded/number/uint.d.mts.map +1 -0
- package/dist/branded/number/uint.mjs +22 -0
- package/dist/branded/number/uint.mjs.map +1 -0
- package/dist/branded/number/uint16.d.mts +3 -0
- package/dist/branded/number/uint16.d.mts.map +1 -0
- package/dist/branded/number/uint16.mjs +26 -0
- package/dist/branded/number/uint16.mjs.map +1 -0
- package/dist/branded/number/uint32.d.mts +3 -0
- package/dist/branded/number/uint32.d.mts.map +1 -0
- package/dist/branded/number/uint32.mjs +24 -0
- package/dist/branded/number/uint32.mjs.map +1 -0
- package/dist/branded/simple-branded-number.d.mts +3 -0
- package/dist/branded/simple-branded-number.d.mts.map +1 -0
- package/dist/branded/simple-branded-number.mjs +15 -0
- package/dist/branded/simple-branded-number.mjs.map +1 -0
- package/dist/branded/simple-branded-string.d.mts +3 -0
- package/dist/branded/simple-branded-string.d.mts.map +1 -0
- package/dist/branded/simple-branded-string.mjs +15 -0
- package/dist/branded/simple-branded-string.mjs.map +1 -0
- package/dist/compose/index.d.mts +4 -0
- package/dist/compose/index.d.mts.map +1 -0
- package/dist/compose/index.mjs +4 -0
- package/dist/compose/index.mjs.map +1 -0
- package/dist/compose/intersection.d.mts +19 -0
- package/dist/compose/intersection.d.mts.map +1 -0
- package/dist/compose/intersection.mjs +38 -0
- package/dist/compose/intersection.mjs.map +1 -0
- package/dist/compose/merge-records.d.mts +15 -0
- package/dist/compose/merge-records.d.mts.map +1 -0
- package/dist/compose/merge-records.mjs +48 -0
- package/dist/compose/merge-records.mjs.map +1 -0
- package/dist/compose/union.d.mts +14 -0
- package/dist/compose/union.d.mts.map +1 -0
- package/dist/compose/union.mjs +33 -0
- package/dist/compose/union.mjs.map +1 -0
- package/dist/enum/enum.d.mts +6 -0
- package/dist/enum/enum.d.mts.map +1 -0
- package/dist/enum/enum.mjs +34 -0
- package/dist/enum/enum.mjs.map +1 -0
- package/dist/enum/index.d.mts +3 -0
- package/dist/enum/index.d.mts.map +1 -0
- package/dist/enum/index.mjs +3 -0
- package/dist/enum/index.mjs.map +1 -0
- package/dist/enum/uint-range.d.mts +8 -0
- package/dist/enum/uint-range.d.mts.map +1 -0
- package/dist/enum/uint-range.mjs +40 -0
- package/dist/enum/uint-range.mjs.map +1 -0
- package/dist/globals.d.mts +3 -0
- package/dist/index.d.mts +10 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +50 -0
- package/dist/index.mjs.map +1 -0
- package/dist/primitives/bigint.d.mts +4 -0
- package/dist/primitives/bigint.d.mts.map +1 -0
- package/dist/primitives/bigint.mjs +12 -0
- package/dist/primitives/bigint.mjs.map +1 -0
- package/dist/primitives/boolean.d.mts +4 -0
- package/dist/primitives/boolean.d.mts.map +1 -0
- package/dist/primitives/boolean.mjs +12 -0
- package/dist/primitives/boolean.mjs.map +1 -0
- package/dist/primitives/index.d.mts +8 -0
- package/dist/primitives/index.d.mts.map +1 -0
- package/dist/primitives/index.mjs +8 -0
- package/dist/primitives/index.mjs.map +1 -0
- package/dist/primitives/null.d.mts +3 -0
- package/dist/primitives/null.d.mts.map +1 -0
- package/dist/primitives/null.mjs +11 -0
- package/dist/primitives/null.mjs.map +1 -0
- package/dist/primitives/number.d.mts +4 -0
- package/dist/primitives/number.d.mts.map +1 -0
- package/dist/primitives/number.mjs +12 -0
- package/dist/primitives/number.mjs.map +1 -0
- package/dist/primitives/string.d.mts +4 -0
- package/dist/primitives/string.d.mts.map +1 -0
- package/dist/primitives/string.mjs +12 -0
- package/dist/primitives/string.mjs.map +1 -0
- package/dist/primitives/symbol.d.mts +3 -0
- package/dist/primitives/symbol.d.mts.map +1 -0
- package/dist/primitives/symbol.mjs +11 -0
- package/dist/primitives/symbol.mjs.map +1 -0
- package/dist/primitives/undefined.d.mts +3 -0
- package/dist/primitives/undefined.d.mts.map +1 -0
- package/dist/primitives/undefined.mjs +11 -0
- package/dist/primitives/undefined.mjs.map +1 -0
- package/dist/record/index.d.mts +8 -0
- package/dist/record/index.d.mts.map +1 -0
- package/dist/record/index.mjs +8 -0
- package/dist/record/index.mjs.map +1 -0
- package/dist/record/key-value-record.d.mts +7 -0
- package/dist/record/key-value-record.d.mts.map +1 -0
- package/dist/record/key-value-record.mjs +52 -0
- package/dist/record/key-value-record.mjs.map +1 -0
- package/dist/record/keyof.d.mts +7 -0
- package/dist/record/keyof.d.mts.map +1 -0
- package/dist/record/keyof.mjs +15 -0
- package/dist/record/keyof.mjs.map +1 -0
- package/dist/record/omit.d.mts +8 -0
- package/dist/record/omit.d.mts.map +1 -0
- package/dist/record/omit.mjs +39 -0
- package/dist/record/omit.mjs.map +1 -0
- package/dist/record/optional.d.mts +5 -0
- package/dist/record/optional.d.mts.map +1 -0
- package/dist/record/optional.mjs +8 -0
- package/dist/record/optional.mjs.map +1 -0
- package/dist/record/partial.d.mts +13 -0
- package/dist/record/partial.d.mts.map +1 -0
- package/dist/record/partial.mjs +128 -0
- package/dist/record/partial.mjs.map +1 -0
- package/dist/record/pick.d.mts +8 -0
- package/dist/record/pick.d.mts.map +1 -0
- package/dist/record/pick.mjs +39 -0
- package/dist/record/pick.mjs.map +1 -0
- package/dist/record/record.d.mts +21 -0
- package/dist/record/record.d.mts.map +1 -0
- package/dist/record/record.mjs +53 -0
- package/dist/record/record.mjs.map +1 -0
- package/dist/tsconfig.json +1 -0
- package/dist/type.d.mts +27 -0
- package/dist/type.d.mts.map +1 -0
- package/dist/type.mjs +2 -0
- package/dist/type.mjs.map +1 -0
- package/dist/types.d.mts +2 -0
- package/dist/unknown.d.mts +3 -0
- package/dist/unknown.d.mts.map +1 -0
- package/dist/unknown.mjs +18 -0
- package/dist/unknown.mjs.map +1 -0
- package/dist/utils/create-assert-fn.d.mts +3 -0
- package/dist/utils/create-assert-fn.d.mts.map +1 -0
- package/dist/utils/create-assert-fn.mjs +12 -0
- package/dist/utils/create-assert-fn.mjs.map +1 -0
- package/dist/utils/create-cast-fn.d.mts +3 -0
- package/dist/utils/create-cast-fn.d.mts.map +1 -0
- package/dist/utils/create-cast-fn.mjs +13 -0
- package/dist/utils/create-cast-fn.mjs.map +1 -0
- package/dist/utils/create-is-fn.d.mts +3 -0
- package/dist/utils/create-is-fn.d.mts.map +1 -0
- package/dist/utils/create-is-fn.mjs +6 -0
- package/dist/utils/create-is-fn.mjs.map +1 -0
- package/dist/utils/create-primitive-type.d.mts +7 -0
- package/dist/utils/create-primitive-type.d.mts.map +1 -0
- package/dist/utils/create-primitive-type.mjs +24 -0
- package/dist/utils/create-primitive-type.mjs.map +1 -0
- package/dist/utils/create-type.d.mts +8 -0
- package/dist/utils/create-type.d.mts.map +1 -0
- package/dist/utils/create-type.mjs +19 -0
- package/dist/utils/create-type.mjs.map +1 -0
- package/dist/utils/index.d.mts +7 -0
- package/dist/utils/index.d.mts.map +1 -0
- package/dist/utils/index.mjs +7 -0
- package/dist/utils/index.mjs.map +1 -0
- package/dist/utils/validation-error-message.d.mts +2 -0
- package/dist/utils/validation-error-message.d.mts.map +1 -0
- package/dist/utils/validation-error-message.mjs +13 -0
- package/dist/utils/validation-error-message.mjs.map +1 -0
- package/package.json +102 -0
- package/src/array/array-of-length.mts +76 -0
- package/src/array/array-of-length.test.mts +113 -0
- package/src/array/array.mts +62 -0
- package/src/array/array.test.mts +84 -0
- package/src/array/index.mts +4 -0
- package/src/array/non-empty-array.mts +70 -0
- package/src/array/non-empty-array.test.mts +113 -0
- package/src/array/tuple.mts +72 -0
- package/src/array/tuple.test.mts +122 -0
- package/src/branded/brand.mts +70 -0
- package/src/branded/index.mts +4 -0
- package/src/branded/number/finite-number.mts +15 -0
- package/src/branded/number/index.mts +16 -0
- package/src/branded/number/int.mts +13 -0
- package/src/branded/number/int16.mts +25 -0
- package/src/branded/number/int32.mts +21 -0
- package/src/branded/number/non-negative-finite-number.mts +18 -0
- package/src/branded/number/non-zero-finite-number.mts +15 -0
- package/src/branded/number/non-zero-int.mts +15 -0
- package/src/branded/number/non-zero-safe-int.mts +15 -0
- package/src/branded/number/positive-finite-number.mts +23 -0
- package/src/branded/number/positive-int.mts +24 -0
- package/src/branded/number/positive-safe-int.mts +25 -0
- package/src/branded/number/safe-int.mts +13 -0
- package/src/branded/number/safe-uint.mts +24 -0
- package/src/branded/number/uint.mts +21 -0
- package/src/branded/number/uint16.mts +25 -0
- package/src/branded/number/uint32.mts +23 -0
- package/src/branded/simple-branded-number.mts +18 -0
- package/src/branded/simple-branded-string.mts +18 -0
- package/src/combination.test.mts +287 -0
- package/src/compose/index.mts +3 -0
- package/src/compose/intersection.mts +206 -0
- package/src/compose/intersection.test.mts +231 -0
- package/src/compose/merge-records.mts +218 -0
- package/src/compose/merge-records.test.mts +90 -0
- package/src/compose/union.mts +77 -0
- package/src/compose/union.test.mts +92 -0
- package/src/enum/enum.mts +53 -0
- package/src/enum/enum.test.mts +63 -0
- package/src/enum/index.mts +2 -0
- package/src/enum/uint-range.mts +63 -0
- package/src/enum/uint-range.test.mts +66 -0
- package/src/globals.d.mts +3 -0
- package/src/index.mts +9 -0
- package/src/primitives/bigint.mts +13 -0
- package/src/primitives/boolean.mts +13 -0
- package/src/primitives/index.mts +7 -0
- package/src/primitives/null.mts +9 -0
- package/src/primitives/number.mts +13 -0
- package/src/primitives/string.mts +13 -0
- package/src/primitives/symbol.mts +10 -0
- package/src/primitives/undefined.mts +9 -0
- package/src/record/index.mts +7 -0
- package/src/record/key-value-record.mts +84 -0
- package/src/record/key-value-record.test.mts +96 -0
- package/src/record/keyof.mts +31 -0
- package/src/record/keyof.test.mts +73 -0
- package/src/record/omit.mts +94 -0
- package/src/record/omit.test.mts +116 -0
- package/src/record/optional.mts +16 -0
- package/src/record/partial.mts +186 -0
- package/src/record/partial.test.mts +261 -0
- package/src/record/pick.mts +98 -0
- package/src/record/pick.test.mts +116 -0
- package/src/record/record.mts +105 -0
- package/src/record/record.test.mts +253 -0
- package/src/type.mts +30 -0
- package/src/unknown.mts +17 -0
- package/src/utils/create-assert-fn.mts +14 -0
- package/src/utils/create-cast-fn.mts +15 -0
- package/src/utils/create-is-fn.mts +7 -0
- package/src/utils/create-primitive-type.mts +35 -0
- package/src/utils/create-type.mts +28 -0
- package/src/utils/index.mts +6 -0
- package/src/utils/validation-error-message.mts +17 -0
package/package.json
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ts-fortress",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"keywords": [
|
|
6
|
+
"typescript"
|
|
7
|
+
],
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/noshiro-pf/ts-fortress.git"
|
|
11
|
+
},
|
|
12
|
+
"license": "Apache-2.0",
|
|
13
|
+
"author": "noshiro-pf <noshiro.pf@gmail.com>",
|
|
14
|
+
"sideEffects": false,
|
|
15
|
+
"type": "module",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"import": {
|
|
19
|
+
"types": "./dist/type.d.mts",
|
|
20
|
+
"default": "./dist/index.mjs"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"module": "./dist/index.mjs",
|
|
25
|
+
"types": "./dist/type.d.mts",
|
|
26
|
+
"files": [
|
|
27
|
+
"src",
|
|
28
|
+
"dist",
|
|
29
|
+
"README.md",
|
|
30
|
+
"LICENSE"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsx ./scripts/cmd/build.mjs",
|
|
34
|
+
"check-all": "tsx ./scripts/cmd/check-all.mjs",
|
|
35
|
+
"check:ext": "tsx ./scripts/cmd/check-ext.mjs",
|
|
36
|
+
"cspell": "cspell \"**\" --gitignore --gitignore-root ./ --no-progress",
|
|
37
|
+
"doc": "tsx ./scripts/cmd/gen-docs.mjs",
|
|
38
|
+
"fmt": "format-diff-from origin/main",
|
|
39
|
+
"fmt:full": "prettier --write .",
|
|
40
|
+
"gi": "gen-index-ts ./src --index-ext .mts --export-ext .mjs --target-ext .mts --target-ext .tsx --fmt 'npm run fmt'",
|
|
41
|
+
"lint": "eslint .",
|
|
42
|
+
"lint:fix": "eslint . --fix",
|
|
43
|
+
"md": "markdownlint-cli2",
|
|
44
|
+
"test": "npm run z:vitest -- run",
|
|
45
|
+
"test:cov": "npm run z:vitest -- run --coverage",
|
|
46
|
+
"test:cov:ui": "vite preview --outDir ./coverage",
|
|
47
|
+
"test:ui": "npm run z:vitest -- --ui",
|
|
48
|
+
"testw": "npm run z:vitest -- watch",
|
|
49
|
+
"tsc": "tsc --noEmit",
|
|
50
|
+
"tscw": "tsc --noEmit --watch -p ./tsconfig.json",
|
|
51
|
+
"type-check": "tsc --noEmit",
|
|
52
|
+
"update-packages": "npx npm-check-updates -u --install always",
|
|
53
|
+
"z:vitest": "vitest --config ./configs/vitest.config.ts"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"ts-data-forge": "^3.0.5",
|
|
57
|
+
"ts-type-forge": "^2.2.0"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@eslint/js": "^9.33.0",
|
|
61
|
+
"@rollup/plugin-replace": "^6.0.2",
|
|
62
|
+
"@rollup/plugin-strip": "^3.0.4",
|
|
63
|
+
"@rollup/plugin-typescript": "^12.1.4",
|
|
64
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
65
|
+
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
66
|
+
"@semantic-release/exec": "^7.1.0",
|
|
67
|
+
"@semantic-release/git": "^10.0.1",
|
|
68
|
+
"@semantic-release/github": "^11.0.4",
|
|
69
|
+
"@semantic-release/npm": "^12.0.2",
|
|
70
|
+
"@semantic-release/release-notes-generator": "^14.0.3",
|
|
71
|
+
"@types/node": "^24.2.1",
|
|
72
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
73
|
+
"@vitest/ui": "^3.2.4",
|
|
74
|
+
"conventional-changelog-conventionalcommits": "^9.1.0",
|
|
75
|
+
"cspell": "^9.2.0",
|
|
76
|
+
"eslint": "^9.33.0",
|
|
77
|
+
"eslint-import-resolver-typescript": "4.4.4",
|
|
78
|
+
"eslint-plugin-array-func": "5.0.2",
|
|
79
|
+
"eslint-plugin-functional": "9.0.2",
|
|
80
|
+
"eslint-plugin-import": "2.32.0",
|
|
81
|
+
"eslint-plugin-prefer-arrow-functions": "3.6.2",
|
|
82
|
+
"eslint-plugin-promise": "7.2.1",
|
|
83
|
+
"eslint-plugin-security": "3.0.1",
|
|
84
|
+
"eslint-plugin-unicorn": "60.0.0",
|
|
85
|
+
"eslint-plugin-vitest": "0.5.4",
|
|
86
|
+
"fast-glob": "^3.3.3",
|
|
87
|
+
"markdownlint-cli2": "^0.18.1",
|
|
88
|
+
"prettier": "^3.6.2",
|
|
89
|
+
"prettier-plugin-organize-imports": "^4.2.0",
|
|
90
|
+
"prettier-plugin-packagejson": "^2.5.19",
|
|
91
|
+
"rollup": "^4.46.2",
|
|
92
|
+
"semantic-release": "^24.2.7",
|
|
93
|
+
"ts-repo-utils": "^6.0.0",
|
|
94
|
+
"tslib": "^2.8.1",
|
|
95
|
+
"tsx": "^4.20.4",
|
|
96
|
+
"typedoc": "^0.28.10",
|
|
97
|
+
"typedoc-plugin-markdown": "^4.8.1",
|
|
98
|
+
"typescript": "^5.9.2",
|
|
99
|
+
"typescript-eslint": "^8.39.1",
|
|
100
|
+
"vitest": "^3.2.4"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Arr, Result } from 'ts-data-forge';
|
|
2
|
+
import { type Type } from '../type.mjs';
|
|
3
|
+
import {
|
|
4
|
+
createAssertFn,
|
|
5
|
+
createCastFn,
|
|
6
|
+
createIsFn,
|
|
7
|
+
validationErrorMessage,
|
|
8
|
+
} from '../utils/index.mjs';
|
|
9
|
+
|
|
10
|
+
export const arrayOfLength = <A, N extends SmallUint>(
|
|
11
|
+
size: N,
|
|
12
|
+
elementType: Type<A>,
|
|
13
|
+
options?: Readonly<{
|
|
14
|
+
typeName?: string;
|
|
15
|
+
defaultValue?: ArrayOfLength<N, A>;
|
|
16
|
+
}>,
|
|
17
|
+
): Type<ArrayOfLength<N, A>> => {
|
|
18
|
+
type T = ArrayOfLength<N, A>;
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
typeName,
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
23
|
+
defaultValue = Arr.create(size, elementType.defaultValue) as T,
|
|
24
|
+
} = options ?? {};
|
|
25
|
+
|
|
26
|
+
const typeNameFilled: string =
|
|
27
|
+
typeName ?? `[${Arr.create(size, elementType.typeName).join(', ')}]`;
|
|
28
|
+
|
|
29
|
+
const validate: Type<T>['validate'] = (a) => {
|
|
30
|
+
if (!Arr.isArray(a)) {
|
|
31
|
+
return Result.err([
|
|
32
|
+
validationErrorMessage(a, 'The value is expected to be an array'),
|
|
33
|
+
]);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (a.length !== size) {
|
|
37
|
+
return Result.err([
|
|
38
|
+
`The value is expected to be an array of length ${size}, but it is ${a.length}.`,
|
|
39
|
+
]);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
for (const [index, el] of a.entries()) {
|
|
43
|
+
const res = elementType.validate(el);
|
|
44
|
+
|
|
45
|
+
if (Result.isErr(res)) {
|
|
46
|
+
const message = validationErrorMessage(
|
|
47
|
+
el,
|
|
48
|
+
`The array element is expected to be <${elementType.typeName}>`,
|
|
49
|
+
(str) => `but the actual value at index ${index} is '${str}'`,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return Result.err([message, ...res.value]);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
57
|
+
return Result.ok(a as unknown as T);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const fill: Type<T>['fill'] = (a) =>
|
|
61
|
+
Arr.isArray(a)
|
|
62
|
+
? // TODO: remove as
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
64
|
+
(Arr.map(Arr.seq(size), (i) => elementType.fill(a[i]) satisfies A) as T)
|
|
65
|
+
: defaultValue;
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
typeName: typeNameFilled,
|
|
69
|
+
defaultValue,
|
|
70
|
+
fill,
|
|
71
|
+
validate,
|
|
72
|
+
is: createIsFn(validate),
|
|
73
|
+
cast: createCastFn(validate),
|
|
74
|
+
assertIs: createAssertFn(validate),
|
|
75
|
+
};
|
|
76
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { expectType } from 'ts-data-forge';
|
|
2
|
+
import { number } from '../primitives/index.mjs';
|
|
3
|
+
import { type TypeOf } from '../type.mjs';
|
|
4
|
+
import { arrayOfLength } from './array-of-length.mjs';
|
|
5
|
+
|
|
6
|
+
describe('arrayOfLength', () => {
|
|
7
|
+
describe('arg patterns', () => {
|
|
8
|
+
test('without explicit default value', () => {
|
|
9
|
+
expect(arrayOfLength(3, number()).defaultValue).toStrictEqual([0, 0, 0]);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('with explicit default value, case 1', () => {
|
|
13
|
+
expect(arrayOfLength(3, number(2)).defaultValue).toStrictEqual([2, 2, 2]);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('with explicit default value, case 2', () => {
|
|
17
|
+
expect(
|
|
18
|
+
arrayOfLength(4, number(0), {
|
|
19
|
+
typeName: 'xs',
|
|
20
|
+
defaultValue: [1, 2, 3, 4],
|
|
21
|
+
}).defaultValue,
|
|
22
|
+
).toStrictEqual([1, 2, 3, 4]);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const xs = arrayOfLength(4, number(0), {
|
|
27
|
+
typeName: 'xs',
|
|
28
|
+
defaultValue: [1, 2, 3, 4],
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
type Xs = TypeOf<typeof xs>;
|
|
32
|
+
|
|
33
|
+
expectType<Xs, ArrayOfLength<4, number>>('=');
|
|
34
|
+
|
|
35
|
+
expectType<typeof xs.defaultValue, Xs>('=');
|
|
36
|
+
|
|
37
|
+
describe('is', () => {
|
|
38
|
+
test('truthy case', () => {
|
|
39
|
+
const ys: unknown = [5, 6, 7, 8];
|
|
40
|
+
|
|
41
|
+
if (xs.is(ys)) {
|
|
42
|
+
expectType<typeof ys, Xs>('=');
|
|
43
|
+
} else {
|
|
44
|
+
expectType<typeof ys, unknown>('=');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
expect(xs.is(ys)).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('falsy case 1', () => {
|
|
51
|
+
const ys: unknown = [];
|
|
52
|
+
|
|
53
|
+
if (xs.is(ys)) {
|
|
54
|
+
expectType<typeof ys, Xs>('=');
|
|
55
|
+
} else {
|
|
56
|
+
expectType<typeof ys, unknown>('=');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
expect(xs.is(ys)).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('falsy case 2', () => {
|
|
63
|
+
const ys: unknown = ['1', '', 3];
|
|
64
|
+
|
|
65
|
+
if (xs.is(ys)) {
|
|
66
|
+
expectType<typeof ys, Xs>('=');
|
|
67
|
+
} else {
|
|
68
|
+
expectType<typeof ys, unknown>('=');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
expect(xs.is(ys)).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('validate', () => {
|
|
76
|
+
test('falsy case 1', () => {
|
|
77
|
+
const ys: unknown = [];
|
|
78
|
+
|
|
79
|
+
expect(xs.validate(ys).value).toStrictEqual([
|
|
80
|
+
'The value is expected to be an array of length 4, but it is 0.',
|
|
81
|
+
]);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('falsy case 2', () => {
|
|
85
|
+
const ys: unknown = [0, '1', '', 3];
|
|
86
|
+
|
|
87
|
+
expect(xs.validate(ys).value).toStrictEqual([
|
|
88
|
+
`The array element is expected to be <number>, but the actual value at index 1 is '"1"'.`,
|
|
89
|
+
`The value is expected to be <number>, but it is actually '"1"'.`,
|
|
90
|
+
]);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('fill', () => {
|
|
95
|
+
test('noop', () => {
|
|
96
|
+
const ys: unknown = [1, 2, 3];
|
|
97
|
+
|
|
98
|
+
expect(xs.fill(ys)).toStrictEqual([1, 2, 3, 0]);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('fill with the default value', () => {
|
|
102
|
+
const ys: unknown = ['1', '', 3];
|
|
103
|
+
|
|
104
|
+
expect(xs.fill(ys)).toStrictEqual([0, 0, 3, 0]);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('fill empty array', () => {
|
|
108
|
+
const ys: unknown = [];
|
|
109
|
+
|
|
110
|
+
expect(xs.fill(ys)).toStrictEqual([0, 0, 0, 0]);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Arr, Result } from 'ts-data-forge';
|
|
2
|
+
import { type Type } from '../type.mjs';
|
|
3
|
+
import {
|
|
4
|
+
createAssertFn,
|
|
5
|
+
createCastFn,
|
|
6
|
+
createIsFn,
|
|
7
|
+
validationErrorMessage,
|
|
8
|
+
} from '../utils/index.mjs';
|
|
9
|
+
|
|
10
|
+
export const array = <A,>(
|
|
11
|
+
elementType: Type<A>,
|
|
12
|
+
options?: Partial<
|
|
13
|
+
Readonly<{
|
|
14
|
+
typeName: string;
|
|
15
|
+
defaultValue: readonly A[];
|
|
16
|
+
}>
|
|
17
|
+
>,
|
|
18
|
+
): Type<readonly A[]> => {
|
|
19
|
+
type T = readonly A[];
|
|
20
|
+
|
|
21
|
+
const { typeName, defaultValue = [] } = options ?? {};
|
|
22
|
+
|
|
23
|
+
const typeNameFilled: string = typeName ?? `(${elementType.typeName})[]`;
|
|
24
|
+
|
|
25
|
+
const validate: Type<T>['validate'] = (a) => {
|
|
26
|
+
if (!Arr.isArray(a)) {
|
|
27
|
+
return Result.err([
|
|
28
|
+
validationErrorMessage(a, 'The value is expected to be an array'),
|
|
29
|
+
]);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for (const [index, el] of a.entries()) {
|
|
33
|
+
const res = elementType.validate(el);
|
|
34
|
+
|
|
35
|
+
if (Result.isErr(res)) {
|
|
36
|
+
const message = validationErrorMessage(
|
|
37
|
+
el,
|
|
38
|
+
`The array element is expected to be <${elementType.typeName}>`,
|
|
39
|
+
(str) => `but the actual value at index ${index} is '${str}'`,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
return Result.err([message, ...res.value]);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
47
|
+
return Result.ok(a as T);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const fill: Type<T>['fill'] = (a) =>
|
|
51
|
+
Arr.isArray(a) ? a.map((e) => elementType.fill(e)) : defaultValue;
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
typeName: typeNameFilled,
|
|
55
|
+
defaultValue,
|
|
56
|
+
fill,
|
|
57
|
+
validate,
|
|
58
|
+
is: createIsFn(validate),
|
|
59
|
+
assertIs: createAssertFn(validate),
|
|
60
|
+
cast: createCastFn(validate),
|
|
61
|
+
};
|
|
62
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { expectType } from 'ts-data-forge';
|
|
2
|
+
import { number } from '../primitives/index.mjs';
|
|
3
|
+
import { type TypeOf } from '../type.mjs';
|
|
4
|
+
import { array } from './array.mjs';
|
|
5
|
+
|
|
6
|
+
describe('array', () => {
|
|
7
|
+
describe('arg patterns', () => {
|
|
8
|
+
test('without explicit default value', () => {
|
|
9
|
+
expect(array(number()).defaultValue).toStrictEqual([]);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('with explicit default value', () => {
|
|
13
|
+
expect(
|
|
14
|
+
array(number(0), {
|
|
15
|
+
typeName: 'xs',
|
|
16
|
+
defaultValue: [],
|
|
17
|
+
}).defaultValue,
|
|
18
|
+
).toStrictEqual([]);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const xs = array(number(0), {
|
|
23
|
+
typeName: 'xs',
|
|
24
|
+
defaultValue: [],
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
type Xs = TypeOf<typeof xs>;
|
|
28
|
+
|
|
29
|
+
expectType<Xs, readonly number[]>('=');
|
|
30
|
+
|
|
31
|
+
expectType<typeof xs.defaultValue, Xs>('=');
|
|
32
|
+
|
|
33
|
+
describe('is', () => {
|
|
34
|
+
test('truthy case', () => {
|
|
35
|
+
const ys: unknown = [1, 2, 3];
|
|
36
|
+
|
|
37
|
+
if (xs.is(ys)) {
|
|
38
|
+
expectType<typeof ys, Xs>('=');
|
|
39
|
+
} else {
|
|
40
|
+
expectType<typeof ys, unknown>('=');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
expect(xs.is(ys)).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('falsy case', () => {
|
|
47
|
+
const ys: unknown = ['1', '', 3];
|
|
48
|
+
|
|
49
|
+
if (xs.is(ys)) {
|
|
50
|
+
expectType<typeof ys, Xs>('=');
|
|
51
|
+
} else {
|
|
52
|
+
expectType<typeof ys, unknown>('=');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
expect(xs.is(ys)).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('validate', () => {
|
|
60
|
+
test('falsy case', () => {
|
|
61
|
+
const ys: unknown = ['1', '', 3];
|
|
62
|
+
|
|
63
|
+
expect(xs.is(ys)).toBe(false);
|
|
64
|
+
expect(xs.validate(ys).value).toStrictEqual([
|
|
65
|
+
`The array element is expected to be <number>, but the actual value at index 0 is '"1"'.`,
|
|
66
|
+
`The value is expected to be <number>, but it is actually '"1"'.`,
|
|
67
|
+
]);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('fill', () => {
|
|
72
|
+
test('noop', () => {
|
|
73
|
+
const ys: unknown = [1, 2, 3];
|
|
74
|
+
|
|
75
|
+
expect(xs.fill(ys)).toStrictEqual([1, 2, 3]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('fill with the default value', () => {
|
|
79
|
+
const ys: unknown = ['1', '', 3];
|
|
80
|
+
|
|
81
|
+
expect(xs.fill(ys)).toStrictEqual([0, 0, 3]);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Arr, Result } from 'ts-data-forge';
|
|
2
|
+
import { type Type } from '../type.mjs';
|
|
3
|
+
import {
|
|
4
|
+
createAssertFn,
|
|
5
|
+
createCastFn,
|
|
6
|
+
createIsFn,
|
|
7
|
+
validationErrorMessage,
|
|
8
|
+
} from '../utils/index.mjs';
|
|
9
|
+
|
|
10
|
+
export const nonEmptyArray = <A,>(
|
|
11
|
+
elementType: Type<A>,
|
|
12
|
+
options?: Readonly<{
|
|
13
|
+
typeName?: string;
|
|
14
|
+
defaultValue?: NonEmptyArray<A>;
|
|
15
|
+
}>,
|
|
16
|
+
): Type<NonEmptyArray<A>> => {
|
|
17
|
+
type T = NonEmptyArray<A>;
|
|
18
|
+
|
|
19
|
+
const { typeName, defaultValue = Arr.newArray(1, elementType.defaultValue) } =
|
|
20
|
+
options ?? {};
|
|
21
|
+
|
|
22
|
+
const typeNameFilled: string =
|
|
23
|
+
typeName ?? `NonEmptyArray<${elementType.typeName}>`;
|
|
24
|
+
|
|
25
|
+
const validate: Type<T>['validate'] = (a) => {
|
|
26
|
+
if (!Arr.isArray(a)) {
|
|
27
|
+
return Result.err([
|
|
28
|
+
validationErrorMessage(a, 'The value is expected to be an array'),
|
|
29
|
+
]);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (Arr.isEmpty(a)) {
|
|
33
|
+
return Result.err([
|
|
34
|
+
'The value is expected to be a non-empty array, but it is empty.',
|
|
35
|
+
]);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for (const [index, el] of a.entries()) {
|
|
39
|
+
const res = elementType.validate(el);
|
|
40
|
+
|
|
41
|
+
if (Result.isErr(res)) {
|
|
42
|
+
const message = validationErrorMessage(
|
|
43
|
+
el,
|
|
44
|
+
`The array element is expected to be <${elementType.typeName}>`,
|
|
45
|
+
(str) => `but the actual value at index ${index} is '${str}'`,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return Result.err([message, ...res.value]);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
53
|
+
return Result.ok(a as unknown as T);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const fill: Type<T>['fill'] = (a) =>
|
|
57
|
+
Arr.isArray(a) && Arr.isNonEmpty(a)
|
|
58
|
+
? Arr.map(a, (e) => elementType.fill(e))
|
|
59
|
+
: defaultValue;
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
typeName: typeNameFilled,
|
|
63
|
+
defaultValue,
|
|
64
|
+
fill,
|
|
65
|
+
validate,
|
|
66
|
+
is: createIsFn(validate),
|
|
67
|
+
assertIs: createAssertFn(validate),
|
|
68
|
+
cast: createCastFn(validate),
|
|
69
|
+
};
|
|
70
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { expectType } from 'ts-data-forge';
|
|
2
|
+
import { number } from '../primitives/index.mjs';
|
|
3
|
+
import { type TypeOf } from '../type.mjs';
|
|
4
|
+
import { nonEmptyArray } from './non-empty-array.mjs';
|
|
5
|
+
|
|
6
|
+
describe('nonEmptyArray', () => {
|
|
7
|
+
describe('arg patterns', () => {
|
|
8
|
+
test('without explicit default value', () => {
|
|
9
|
+
expect(nonEmptyArray(number()).defaultValue).toStrictEqual([0]);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('with explicit default value, case 1', () => {
|
|
13
|
+
expect(nonEmptyArray(number(1)).defaultValue).toStrictEqual([1]);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('with explicit default value, case 2', () => {
|
|
17
|
+
expect(
|
|
18
|
+
nonEmptyArray(number(0), {
|
|
19
|
+
typeName: 'xs',
|
|
20
|
+
defaultValue: [2],
|
|
21
|
+
}).defaultValue,
|
|
22
|
+
).toStrictEqual([2]);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const xs = nonEmptyArray(number(0), {
|
|
27
|
+
defaultValue: [1],
|
|
28
|
+
typeName: 'xs',
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
type Xs = TypeOf<typeof xs>;
|
|
32
|
+
|
|
33
|
+
expectType<Xs, NonEmptyArray<number>>('=');
|
|
34
|
+
|
|
35
|
+
expectType<typeof xs.defaultValue, Xs>('=');
|
|
36
|
+
|
|
37
|
+
describe('is', () => {
|
|
38
|
+
test('truthy case', () => {
|
|
39
|
+
const ys: unknown = [1, 2, 3];
|
|
40
|
+
|
|
41
|
+
if (xs.is(ys)) {
|
|
42
|
+
expectType<typeof ys, Xs>('=');
|
|
43
|
+
} else {
|
|
44
|
+
expectType<typeof ys, unknown>('=');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
expect(xs.is(ys)).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('falsy case 1', () => {
|
|
51
|
+
const ys: unknown = [];
|
|
52
|
+
|
|
53
|
+
if (xs.is(ys)) {
|
|
54
|
+
expectType<typeof ys, Xs>('=');
|
|
55
|
+
} else {
|
|
56
|
+
expectType<typeof ys, unknown>('=');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
expect(xs.is(ys)).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('falsy case 2', () => {
|
|
63
|
+
const ys: unknown = ['1', '', 3];
|
|
64
|
+
|
|
65
|
+
if (xs.is(ys)) {
|
|
66
|
+
expectType<typeof ys, Xs>('=');
|
|
67
|
+
} else {
|
|
68
|
+
expectType<typeof ys, unknown>('=');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
expect(xs.is(ys)).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('validate', () => {
|
|
76
|
+
test('falsy case 1', () => {
|
|
77
|
+
const ys: unknown = [];
|
|
78
|
+
|
|
79
|
+
expect(xs.validate(ys).value).toStrictEqual([
|
|
80
|
+
'The value is expected to be a non-empty array, but it is empty.',
|
|
81
|
+
]);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('falsy case 2', () => {
|
|
85
|
+
const ys: unknown = ['1', '', 3];
|
|
86
|
+
|
|
87
|
+
expect(xs.validate(ys).value).toStrictEqual([
|
|
88
|
+
`The array element is expected to be <number>, but the actual value at index 0 is '"1"'.`,
|
|
89
|
+
`The value is expected to be <number>, but it is actually '"1"'.`,
|
|
90
|
+
]);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('fill', () => {
|
|
95
|
+
test('noop', () => {
|
|
96
|
+
const ys: unknown = [1, 2, 3];
|
|
97
|
+
|
|
98
|
+
expect(xs.fill(ys)).toStrictEqual([1, 2, 3]);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('fill with the default value', () => {
|
|
102
|
+
const ys: unknown = ['1', '', 3];
|
|
103
|
+
|
|
104
|
+
expect(xs.fill(ys)).toStrictEqual([0, 0, 3]);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('fill empty array', () => {
|
|
108
|
+
const ys: unknown = [];
|
|
109
|
+
|
|
110
|
+
expect(xs.fill(ys)).toStrictEqual([1]);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Arr, Result } from 'ts-data-forge';
|
|
2
|
+
import { type Type, type TypeOf } from '../type.mjs';
|
|
3
|
+
import {
|
|
4
|
+
createAssertFn,
|
|
5
|
+
createCastFn,
|
|
6
|
+
createIsFn,
|
|
7
|
+
validationErrorMessage,
|
|
8
|
+
} from '../utils/index.mjs';
|
|
9
|
+
|
|
10
|
+
type MapTuple<T extends readonly Type<unknown>[]> = {
|
|
11
|
+
readonly [K in keyof T]: TypeOf<T[K]>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const tuple = <const A extends readonly Type<unknown>[]>(
|
|
15
|
+
types: A,
|
|
16
|
+
options?: Partial<Readonly<{ typeName?: string }>>,
|
|
17
|
+
): Type<MapTuple<A>> => {
|
|
18
|
+
type T = MapTuple<A>;
|
|
19
|
+
|
|
20
|
+
const { typeName = 'tuple' } = options ?? {};
|
|
21
|
+
|
|
22
|
+
const defaultValue = Arr.map(
|
|
23
|
+
types,
|
|
24
|
+
(t) => t.defaultValue,
|
|
25
|
+
) satisfies MapTuple<A>;
|
|
26
|
+
|
|
27
|
+
const validate: Type<T>['validate'] = (a) => {
|
|
28
|
+
if (!Arr.isArray(a)) {
|
|
29
|
+
return Result.err([
|
|
30
|
+
validationErrorMessage(a, 'The value is expected to be an array'),
|
|
31
|
+
]);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (a.length !== types.length) {
|
|
35
|
+
return Result.err([
|
|
36
|
+
`The length of tuple is expected to be ${types.length}, but it is actually ${a.length}.`,
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
for (const [index, [typeDef, el]] of Arr.zip(types, a).entries()) {
|
|
41
|
+
const res = typeDef.validate(el);
|
|
42
|
+
|
|
43
|
+
if (Result.isErr(res)) {
|
|
44
|
+
const message = validationErrorMessage(
|
|
45
|
+
el,
|
|
46
|
+
`The tuple element at ${index} is expected to be <${typeDef.typeName}>`,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return Result.err([message, ...res.value]);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
54
|
+
return Result.ok(a as T);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const fill: Type<T>['fill'] = (a) =>
|
|
58
|
+
!Arr.isArray(a)
|
|
59
|
+
? defaultValue
|
|
60
|
+
: // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
61
|
+
(types.map((t, i) => t.fill(a[i])) as MapTuple<A>);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
typeName,
|
|
65
|
+
defaultValue,
|
|
66
|
+
fill,
|
|
67
|
+
validate,
|
|
68
|
+
is: createIsFn(validate),
|
|
69
|
+
assertIs: createAssertFn(validate),
|
|
70
|
+
cast: createCastFn(validate),
|
|
71
|
+
};
|
|
72
|
+
};
|