ts-data-forge 14.4.0 → 14.5.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 +6 -0
- package/dist/array/impl/array-utils-search.d.mts.map +1 -1
- package/dist/array/impl/array-utils-search.mjs.map +1 -1
- package/dist/functional/optional/impl/index.d.mts +1 -0
- package/dist/functional/optional/impl/index.d.mts.map +1 -1
- package/dist/functional/optional/impl/index.mjs +1 -0
- package/dist/functional/optional/impl/index.mjs.map +1 -1
- package/dist/functional/optional/impl/optional-match.d.mts +48 -0
- package/dist/functional/optional/impl/optional-match.d.mts.map +1 -0
- package/dist/functional/optional/impl/optional-match.mjs +22 -0
- package/dist/functional/optional/impl/optional-match.mjs.map +1 -0
- package/dist/functional/result/impl/index.d.mts +4 -0
- package/dist/functional/result/impl/index.d.mts.map +1 -1
- package/dist/functional/result/impl/index.mjs +4 -0
- package/dist/functional/result/impl/index.mjs.map +1 -1
- package/dist/functional/result/impl/result-from-optional.d.mts +41 -0
- package/dist/functional/result/impl/result-from-optional.d.mts.map +1 -0
- package/dist/functional/result/impl/result-from-optional.mjs +24 -0
- package/dist/functional/result/impl/result-from-optional.mjs.map +1 -0
- package/dist/functional/result/impl/result-match.d.mts +47 -0
- package/dist/functional/result/impl/result-match.d.mts.map +1 -0
- package/dist/functional/result/impl/result-match.mjs +24 -0
- package/dist/functional/result/impl/result-match.mjs.map +1 -0
- package/dist/functional/result/impl/result-safe-try.d.mts +51 -0
- package/dist/functional/result/impl/result-safe-try.d.mts.map +1 -0
- package/dist/functional/result/impl/result-safe-try.mjs +24 -0
- package/dist/functional/result/impl/result-safe-try.mjs.map +1 -0
- package/dist/functional/result/impl/result-safe-unwrap.d.mts +36 -0
- package/dist/functional/result/impl/result-safe-unwrap.d.mts.map +1 -0
- package/dist/functional/result/impl/result-safe-unwrap.mjs +14 -0
- package/dist/functional/result/impl/result-safe-unwrap.mjs.map +1 -0
- package/dist/others/debounce-function.d.mts.map +1 -1
- package/dist/others/debounce-function.mjs.map +1 -1
- package/dist/others/fast-deep-equal.d.mts.map +1 -1
- package/dist/others/fast-deep-equal.mjs +1 -1
- package/dist/others/fast-deep-equal.mjs.map +1 -1
- package/package.json +9 -9
- package/src/array/impl/array-utils-modification.test.mts +3 -3
- package/src/array/impl/array-utils-search.mts +22 -8
- package/src/array/impl/array-utils-shape-invariants.test.mts +3 -3
- package/src/array/impl/array-utils-transformation.test.mts +1 -1
- package/src/functional/optional/impl/index.mts +1 -0
- package/src/functional/optional/impl/optional-match.mts +108 -0
- package/src/functional/optional.test.mts +79 -0
- package/src/functional/result/impl/index.mts +4 -0
- package/src/functional/result/impl/result-from-optional.mts +80 -0
- package/src/functional/result/impl/result-match.mts +108 -0
- package/src/functional/result/impl/result-safe-try.mts +110 -0
- package/src/functional/result/impl/result-safe-unwrap.mts +50 -0
- package/src/functional/result.test.mts +331 -0
- package/src/guard/is-error.test.mts +7 -7
- package/src/others/debounce-function.mts +5 -1
- package/src/others/fast-deep-equal.mts +9 -2
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { isErr } from './result-is-err.mjs';
|
|
2
|
+
|
|
3
|
+
function* safeUnwrap(result) {
|
|
4
|
+
if (isErr(result)) {
|
|
5
|
+
yield result;
|
|
6
|
+
throw new Error(
|
|
7
|
+
"`safeUnwrap()` generator was resumed after yielding an `Err`; it must only be used via `yield*` inside `safeTry()`"
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
return result.value;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export { safeUnwrap };
|
|
14
|
+
//# sourceMappingURL=result-safe-unwrap.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-safe-unwrap.mjs","sources":["../../../../src/functional/result/impl/result-safe-unwrap.mts"],"sourcesContent":["import { type UnknownResult } from '../result.mjs';\nimport { isErr } from './result-is-err.mjs';\nimport { type NarrowToErr, type UnwrapOk } from './types.mjs';\n\n/**\n * Unwraps a `Result` inside a {@link Result.safeTry} generator body via\n * `yield*`, emulating Rust's `?` operator.\n *\n * If the `Result` is `Ok`, the `yield*` expression evaluates to the contained\n * value and execution continues. If it is `Err`, the error is yielded to\n * {@link Result.safeTry}, which returns it immediately — the rest of the\n * generator body never runs.\n *\n * This generator must only be delegated to (`yield*`) inside a\n * {@link Result.safeTry} body; resuming it after it has yielded an `Err`\n * throws.\n *\n * @example\n *\n * ```ts\n * const result = Result.safeTry(function* () {\n * const x = yield* Result.safeUnwrap(Result.ok(2));\n *\n * const y = yield* Result.safeUnwrap(Result.ok(3));\n *\n * return Result.ok(x + y);\n * });\n *\n * assert.deepStrictEqual(result, Result.ok(5));\n * ```\n *\n * @template R The input `UnknownResult` type.\n * @param result The `Result` to unwrap.\n * @returns A generator that yields the `Err` (if any) and returns the `Ok`\n * value.\n */\nexport function* safeUnwrap<R extends UnknownResult>(\n result: R,\n): Generator<NarrowToErr<R>, UnwrapOk<R>, unknown> {\n if (isErr(result)) {\n yield result;\n\n throw new Error(\n '`safeUnwrap()` generator was resumed after yielding an `Err`; it must only be used via `yield*` inside `safeTry()`',\n );\n }\n\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n return result.value as UnwrapOk<R>;\n}\n"],"names":[],"mappings":";;AAoCO,UAAU,WACf,MAAA,EACiD;AACjD,EAAA,IAAI,KAAA,CAAM,MAAM,CAAA,EAAG;AACjB,IAAA,MAAM,MAAA;AAEN,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AAGA,EAAA,OAAO,MAAA,CAAO,KAAA;AAChB;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debounce-function.d.mts","sourceRoot":"","sources":["../../src/others/debounce-function.mts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,GAAI,IAAI,SAAS,SAAS,OAAO,EAAE,QAChD,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,IAAI,oBACX,MAAM,KACvB,CAAC,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,IAAI,
|
|
1
|
+
{"version":3,"file":"debounce-function.d.mts","sourceRoot":"","sources":["../../src/others/debounce-function.mts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,GAAI,IAAI,SAAS,SAAS,OAAO,EAAE,QAChD,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,IAAI,oBACX,MAAM,KACvB,CAAC,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,IAAI,CAc1B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debounce-function.mjs","sources":["../../src/others/debounce-function.mts"],"sourcesContent":["/**\n * Creates a debounced function that delays invoking `func` until after `waitMilliseconds`\n * have elapsed since the last time the debounced function was invoked.\n *\n * @template F The type of the function to debounce.\n * @param func The function to debounce.\n * @param waitMilliseconds The number of milliseconds to delay.\n * @returns The new debounced function.\n */\nexport const debounce = <Args extends readonly unknown[]>(\n func: (...args: Args) => void,\n waitMilliseconds: number,\n): ((...args: Args) => void) => {\n let mut_timeoutId:
|
|
1
|
+
{"version":3,"file":"debounce-function.mjs","sources":["../../src/others/debounce-function.mts"],"sourcesContent":["/**\n * Creates a debounced function that delays invoking `func` until after `waitMilliseconds`\n * have elapsed since the last time the debounced function was invoked.\n *\n * @template F The type of the function to debounce.\n * @param func The function to debounce.\n * @param waitMilliseconds The number of milliseconds to delay.\n * @returns The new debounced function.\n */\nexport const debounce = <Args extends readonly unknown[]>(\n func: (...args: Args) => void,\n waitMilliseconds: number,\n): ((...args: Args) => void) => {\n // Not `ReturnType<typeof setTimeout>`: with both the DOM and Node\n // declarations in scope that resolves to `unknown`, which `clearTimeout`\n // will not take. Naming what `clearTimeout` accepts says the same thing and\n // stays true in either environment.\n let mut_timeoutId: Parameters<typeof clearTimeout>[0];\n\n return (...args: Args): void => {\n clearTimeout(mut_timeoutId);\n\n mut_timeoutId = setTimeout(() => {\n func(...args);\n }, waitMilliseconds);\n };\n};\n"],"names":[],"mappings":"AASO,MAAM,QAAA,GAAW,CACtB,IAAA,EACA,gBAAA,KAC8B;AAK9B,EAAA,IAAI,aAAA;AAEJ,EAAA,OAAO,IAAI,IAAA,KAAqB;AAC9B,IAAA,YAAA,CAAa,aAAa,CAAA;AAE1B,IAAA,aAAA,GAAgB,WAAW,MAAM;AAC/B,MAAA,IAAA,CAAK,GAAG,IAAI,CAAA;AAAA,IACd,GAAG,gBAAgB,CAAA;AAAA,EACrB,CAAA;AACF;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fast-deep-equal.d.mts","sourceRoot":"","sources":["../../src/others/fast-deep-equal.mts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,KAAM,CAAC,KAAK,CAAC,KAAG,
|
|
1
|
+
{"version":3,"file":"fast-deep-equal.d.mts","sourceRoot":"","sources":["../../src/others/fast-deep-equal.mts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,KAAM,CAAC,KAAK,CAAC,KAAG,OAgG9C,CAAC"}
|
|
@@ -49,7 +49,7 @@ const fastDeepEqual = (a, b) => {
|
|
|
49
49
|
!(key === "_owner" && a["$$typeof"]) && fastDeepEqual(a[key], b[key])
|
|
50
50
|
);
|
|
51
51
|
}
|
|
52
|
-
return Number.isNaN(a) && Number.isNaN(b);
|
|
52
|
+
return typeof a === "number" && typeof b === "number" && Number.isNaN(a) && Number.isNaN(b);
|
|
53
53
|
};
|
|
54
54
|
|
|
55
55
|
export { fastDeepEqual };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fast-deep-equal.mjs","sources":["../../src/others/fast-deep-equal.mts"],"sourcesContent":["import { isMap, isRegExp, isSet, isTypedArray } from '@sindresorhus/is';\nimport { hasKey, isRecord } from '../guard/index.mjs';\n\n/**\n * The fastest deep equal with ES6 Map, Set and Typed arrays support.\n * Checks equality of Date and RegExp objects by value.\n *\n * Forked from https://github.com/epoberezkin/fast-deep-equal/blob/v3.1.3/src/index.jst\n */\nexport const fastDeepEqual = <T,>(a: T, b: T): boolean => {\n if (a === b) {\n return true;\n }\n\n if (typeof a !== typeof b) {\n return false;\n }\n\n // Arrays are handled before `isRecord`, which excludes them.\n if (Array.isArray(a)) {\n return (\n Array.isArray(b) &&\n a.constructor === b.constructor &&\n a.length === b.length &&\n a.every((ai, index) => fastDeepEqual(ai, b[index]))\n );\n }\n\n if (isRecord(a)) {\n if (!isRecord(b)) {\n return false;\n }\n\n if (a.constructor !== b.constructor) {\n return false;\n }\n\n if (isMap(a)) {\n return (\n isMap(b) &&\n a.size === b.size &&\n a\n .entries()\n .every(\n ([key, value]) => b.has(key) && fastDeepEqual(value, b.get(key)),\n )\n );\n }\n\n if (isSet(a)) {\n return (\n isSet(b) &&\n a.size === b.size &&\n a.entries().every(([value]) => b.has(value))\n );\n }\n\n if (isTypedArray(a)) {\n return (\n isTypedArray(b) &&\n a.length === b.length &&\n a.every((value, index) => value === b[index])\n );\n }\n\n if (isRegExp(a)) {\n return isRegExp(b) && a.source === b.source && a.flags === b.flags;\n }\n\n if (a.valueOf !== Object.prototype.valueOf) {\n return a.valueOf() === b.valueOf();\n }\n\n if (a.toString !== Object.prototype.toString) {\n return a.toString() === b.toString();\n }\n\n const aKeys: readonly string[] = Object.keys(a);\n\n const bKeys: readonly string[] = Object.keys(b);\n\n return (\n aKeys.length === bKeys.length &&\n aKeys.every(\n (key) =>\n hasKey(b, key) &&\n // React-specific: avoid traversing React elements' _owner.\n // _owner contains circular references\n // and is not needed when comparing the actual elements (and not their owners)\n // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions\n !(key === '_owner' && a['$$typeof']) &&\n fastDeepEqual(a[key], b[key]),\n )\n );\n }\n\n // true if both NaN, false otherwise\n return Number.isNaN(a)
|
|
1
|
+
{"version":3,"file":"fast-deep-equal.mjs","sources":["../../src/others/fast-deep-equal.mts"],"sourcesContent":["import { isMap, isRegExp, isSet, isTypedArray } from '@sindresorhus/is';\nimport { hasKey, isRecord } from '../guard/index.mjs';\n\n/**\n * The fastest deep equal with ES6 Map, Set and Typed arrays support.\n * Checks equality of Date and RegExp objects by value.\n *\n * Forked from https://github.com/epoberezkin/fast-deep-equal/blob/v3.1.3/src/index.jst\n */\nexport const fastDeepEqual = <T,>(a: T, b: T): boolean => {\n if (a === b) {\n return true;\n }\n\n if (typeof a !== typeof b) {\n return false;\n }\n\n // Arrays are handled before `isRecord`, which excludes them.\n if (Array.isArray(a)) {\n return (\n Array.isArray(b) &&\n a.constructor === b.constructor &&\n a.length === b.length &&\n a.every((ai, index) => fastDeepEqual(ai, b[index]))\n );\n }\n\n if (isRecord(a)) {\n if (!isRecord(b)) {\n return false;\n }\n\n if (a.constructor !== b.constructor) {\n return false;\n }\n\n if (isMap(a)) {\n return (\n isMap(b) &&\n a.size === b.size &&\n a\n .entries()\n .every(\n ([key, value]) => b.has(key) && fastDeepEqual(value, b.get(key)),\n )\n );\n }\n\n if (isSet(a)) {\n return (\n isSet(b) &&\n a.size === b.size &&\n a.entries().every(([value]) => b.has(value))\n );\n }\n\n if (isTypedArray(a)) {\n return (\n isTypedArray(b) &&\n a.length === b.length &&\n a.every((value, index) => value === b[index])\n );\n }\n\n if (isRegExp(a)) {\n return isRegExp(b) && a.source === b.source && a.flags === b.flags;\n }\n\n if (a.valueOf !== Object.prototype.valueOf) {\n return a.valueOf() === b.valueOf();\n }\n\n if (a.toString !== Object.prototype.toString) {\n return a.toString() === b.toString();\n }\n\n const aKeys: readonly string[] = Object.keys(a);\n\n const bKeys: readonly string[] = Object.keys(b);\n\n return (\n aKeys.length === bKeys.length &&\n aKeys.every(\n (key) =>\n hasKey(b, key) &&\n // React-specific: avoid traversing React elements' _owner.\n // _owner contains circular references\n // and is not needed when comparing the actual elements (and not their owners)\n // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions\n !(key === '_owner' && a['$$typeof']) &&\n fastDeepEqual(a[key], b[key]),\n )\n );\n }\n\n // true if both NaN, false otherwise. `Number.isNaN` takes a number: called\n // with anything else it is always false, so the check belongs behind a\n // `typeof` guard rather than relying on that.\n return (\n typeof a === 'number' &&\n typeof b === 'number' &&\n Number.isNaN(a) &&\n Number.isNaN(b)\n );\n};\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-data-forge",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.5.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -37,29 +37,29 @@
|
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@sindresorhus/is": "^8.1.0",
|
|
40
|
-
"ts-type-forge": "~9.2.
|
|
40
|
+
"ts-type-forge": "~9.2.2"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@emotion/react": "11.14.0",
|
|
44
44
|
"@emotion/styled": "11.14.1",
|
|
45
45
|
"@mui/material": "9.3.1",
|
|
46
46
|
"@types/react": "19.2.18",
|
|
47
|
-
"@vitest/browser-playwright": "4.1.
|
|
47
|
+
"@vitest/browser-playwright": "4.1.11",
|
|
48
48
|
"dedent": "1.7.2",
|
|
49
|
-
"
|
|
49
|
+
"eslint-config-typed": "5.10.0",
|
|
50
|
+
"eslint-plugin-ts-fortress": "1.0.5",
|
|
51
|
+
"eslint-plugin-ts-type-forge": "0.7.1",
|
|
52
|
+
"immer": "11.1.18",
|
|
50
53
|
"playwright": "1.62.1",
|
|
51
54
|
"prettier": "3.9.6",
|
|
52
55
|
"prettier-plugin-organize-imports": "4.3.0",
|
|
53
56
|
"prettier-plugin-packagejson": "3.0.2",
|
|
54
57
|
"react": "19.2.8",
|
|
55
58
|
"rollup": "4.62.4",
|
|
59
|
+
"ts-repo-utils": "10.3.2",
|
|
56
60
|
"typedoc": "0.28.20",
|
|
57
61
|
"typedoc-github-theme": "0.4.0",
|
|
58
|
-
"vite": "8.2.1"
|
|
59
|
-
"eslint-plugin-ts-fortress": "1.0.4",
|
|
60
|
-
"eslint-config-typed": "5.9.3",
|
|
61
|
-
"ts-repo-utils": "10.2.0",
|
|
62
|
-
"eslint-plugin-ts-type-forge": "0.6.6"
|
|
62
|
+
"vite": "8.2.1"
|
|
63
63
|
},
|
|
64
64
|
"peerDependencies": {
|
|
65
65
|
"typescript": ">=4.8"
|
|
@@ -257,7 +257,7 @@ describe('Arr modifications', () => {
|
|
|
257
257
|
xs: T,
|
|
258
258
|
): Readonly<{ [K in keyof T]: 0 }> => toFilled(xs, 0);
|
|
259
259
|
|
|
260
|
-
const result = blank([1, 2, 3]
|
|
260
|
+
const result = blank([1, 2, 3]);
|
|
261
261
|
|
|
262
262
|
expectType<typeof result, FixedLengthTuple<3, 0>>('=');
|
|
263
263
|
|
|
@@ -269,7 +269,7 @@ describe('Arr modifications', () => {
|
|
|
269
269
|
xs: T,
|
|
270
270
|
): Readonly<{ [K in keyof T]: 0 }> => toFilled(0)(xs);
|
|
271
271
|
|
|
272
|
-
const result = blank([1, 2]
|
|
272
|
+
const result = blank([1, 2]);
|
|
273
273
|
|
|
274
274
|
expectType<typeof result, FixedLengthTuple<2, 0>>('=');
|
|
275
275
|
|
|
@@ -319,7 +319,7 @@ describe('Arr modifications', () => {
|
|
|
319
319
|
xs: T,
|
|
320
320
|
): Readonly<{ [K in keyof T]: number }> => toRangeFilled(xs, 0, [1, 2]);
|
|
321
321
|
|
|
322
|
-
const result = blankMiddle([1, 2, 3]
|
|
322
|
+
const result = blankMiddle([1, 2, 3]);
|
|
323
323
|
|
|
324
324
|
expectType<typeof result, FixedLengthTuple<3, number>>('=');
|
|
325
325
|
|
|
@@ -79,10 +79,17 @@ const findImpl = <E,>(
|
|
|
79
79
|
predicate as () => boolean,
|
|
80
80
|
);
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
// Left as a comment rather than asserted. This pinned the shape of the
|
|
83
|
+
// standard library's `findIndex`, and the shape differs between the stock
|
|
84
|
+
// library and the strict one — `(value, index, obj: this) => boolean` there
|
|
85
|
+
// against `(value, index, arr: readonly E[]) => unknown` here. `src` ships
|
|
86
|
+
// to consumers so that "Go to Definition" reaches it, so an assertion that
|
|
87
|
+
// only holds under one of them shows up as an error in their editor.
|
|
88
|
+
//
|
|
89
|
+
// expectType<
|
|
90
|
+
// Parameters<typeof array.findIndex>[0],
|
|
91
|
+
// (value: E, index: number, arr: readonly E[]) => unknown
|
|
92
|
+
// >('=');
|
|
86
93
|
|
|
87
94
|
expectType<
|
|
88
95
|
typeof predicate,
|
|
@@ -162,10 +169,17 @@ const findLastImpl = <E,>(
|
|
|
162
169
|
predicate as () => boolean,
|
|
163
170
|
);
|
|
164
171
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
172
|
+
// Left as a comment rather than asserted. This pinned the shape of the
|
|
173
|
+
// standard library's `findIndex`, and the shape differs between the stock
|
|
174
|
+
// library and the strict one — `(value, index, obj: this) => boolean` there
|
|
175
|
+
// against `(value, index, arr: readonly E[]) => unknown` here. `src` ships
|
|
176
|
+
// to consumers so that "Go to Definition" reaches it, so an assertion that
|
|
177
|
+
// only holds under one of them shows up as an error in their editor.
|
|
178
|
+
//
|
|
179
|
+
// expectType<
|
|
180
|
+
// Parameters<typeof array.findIndex>[0],
|
|
181
|
+
// (value: E, index: number, arr: readonly E[]) => unknown
|
|
182
|
+
// >('=');
|
|
169
183
|
|
|
170
184
|
expectType<
|
|
171
185
|
typeof predicate,
|
|
@@ -267,7 +267,7 @@ describe('Arr shape invariants', () => {
|
|
|
267
267
|
);
|
|
268
268
|
|
|
269
269
|
assert.deepStrictEqual<readonly string[]>(
|
|
270
|
-
toSortedNonNumberInGenericContext(['c', 'a', 'b']
|
|
270
|
+
toSortedNonNumberInGenericContext(['c', 'a', 'b']),
|
|
271
271
|
['a', 'b', 'c'],
|
|
272
272
|
);
|
|
273
273
|
|
|
@@ -313,12 +313,12 @@ describe('Arr shape invariants', () => {
|
|
|
313
313
|
);
|
|
314
314
|
|
|
315
315
|
assert.deepStrictEqual<readonly number[]>(
|
|
316
|
-
uniqInGenericContext([1, 1, 2]
|
|
316
|
+
uniqInGenericContext([1, 1, 2]),
|
|
317
317
|
[1, 2],
|
|
318
318
|
);
|
|
319
319
|
|
|
320
320
|
assert.deepStrictEqual<readonly number[]>(
|
|
321
|
-
uniqByInGenericContext([1, 1, 2]
|
|
321
|
+
uniqByInGenericContext([1, 1, 2]),
|
|
322
322
|
[1, 2],
|
|
323
323
|
);
|
|
324
324
|
});
|
|
@@ -77,7 +77,7 @@ describe('Arr transformations', () => {
|
|
|
77
77
|
boxes: T,
|
|
78
78
|
): Readonly<{ [K in keyof T]: unknown }> => map(boxes, (b) => b.v);
|
|
79
79
|
|
|
80
|
-
const result = mapValues([{ v: 1 }, { v: 'a' }]
|
|
80
|
+
const result = mapValues([{ v: 1 }, { v: 'a' }]);
|
|
81
81
|
|
|
82
82
|
expectType<typeof result, FixedLengthTuple<2, unknown>>('=');
|
|
83
83
|
|
|
@@ -6,6 +6,7 @@ export * from './optional-is-none.mjs';
|
|
|
6
6
|
export * from './optional-is-optional.mjs';
|
|
7
7
|
export * from './optional-is-some.mjs';
|
|
8
8
|
export * from './optional-map.mjs';
|
|
9
|
+
export * from './optional-match.mjs';
|
|
9
10
|
export * from './optional-none.mjs';
|
|
10
11
|
export * from './optional-or-else.mjs';
|
|
11
12
|
export * from './optional-some.mjs';
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { type UnknownOptional } from '../optional.mjs';
|
|
2
|
+
import { isSome } from './optional-is-some.mjs';
|
|
3
|
+
import { unwrap } from './optional-unwrap.mjs';
|
|
4
|
+
import { type Unwrap } from './types.mjs';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Folds an {@link Optional} into a plain value by applying one of two case
|
|
8
|
+
* handlers: `some` if the {@link Optional} contains a value, `none`
|
|
9
|
+
* otherwise. This is the function form of pattern matching on an
|
|
10
|
+
* {@link Optional}.
|
|
11
|
+
*
|
|
12
|
+
* Unlike {@link Optional.map}, the result is not wrapped in an
|
|
13
|
+
* {@link Optional} — both branches produce the final value directly.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* const doubledOrZero = Optional.match(Optional.some(21), {
|
|
19
|
+
* some: (value) => value * 2,
|
|
20
|
+
* none: () => 0,
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* assert.deepStrictEqual(doubledOrZero, 42);
|
|
24
|
+
*
|
|
25
|
+
* const matcher = Optional.match({
|
|
26
|
+
* some: (value: number) => `value: ${value}`,
|
|
27
|
+
* none: () => 'none',
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* assert.deepStrictEqual(matcher(Optional.some(3)), 'value: 3');
|
|
31
|
+
*
|
|
32
|
+
* assert.deepStrictEqual(matcher(Optional.none), 'none');
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @template O The input `UnknownOptional` type.
|
|
36
|
+
* @template S2 The type returned by the `some` case.
|
|
37
|
+
* @template N2 The type returned by the `none` case.
|
|
38
|
+
* @param optional The `Optional` to match on.
|
|
39
|
+
* @param cases An object with a `some` handler receiving the contained value
|
|
40
|
+
* and a `none` handler receiving nothing.
|
|
41
|
+
* @returns The value produced by whichever case handler was applied.
|
|
42
|
+
*/
|
|
43
|
+
export function match<O extends UnknownOptional, S2, N2>(
|
|
44
|
+
optional: O,
|
|
45
|
+
cases: Readonly<{
|
|
46
|
+
some: (value: Unwrap<O>) => S2;
|
|
47
|
+
none: () => N2;
|
|
48
|
+
}>,
|
|
49
|
+
): N2 | S2;
|
|
50
|
+
|
|
51
|
+
// Curried version
|
|
52
|
+
//
|
|
53
|
+
// The returned function is generic over the *whole* optional, mirroring the
|
|
54
|
+
// direct overload above; see the comment on the curried overload of
|
|
55
|
+
// `Optional.map` for why the parameter is a conditional type instead of a
|
|
56
|
+
// plain `Optional<S>`.
|
|
57
|
+
export function match<S, S2, N2>(
|
|
58
|
+
cases: Readonly<{
|
|
59
|
+
some: (value: S) => S2;
|
|
60
|
+
none: () => N2;
|
|
61
|
+
}>,
|
|
62
|
+
): <O extends UnknownOptional>(
|
|
63
|
+
optional: Unwrap<O> extends S ? O : never,
|
|
64
|
+
) => N2 | S2;
|
|
65
|
+
|
|
66
|
+
export function match<O extends UnknownOptional, S2, N2>(
|
|
67
|
+
...args:
|
|
68
|
+
| readonly [
|
|
69
|
+
optional: O,
|
|
70
|
+
cases: Readonly<{
|
|
71
|
+
some: (value: Unwrap<O>) => S2;
|
|
72
|
+
none: () => N2;
|
|
73
|
+
}>,
|
|
74
|
+
]
|
|
75
|
+
| readonly [
|
|
76
|
+
cases: Readonly<{
|
|
77
|
+
some: (value: Unwrap<O>) => S2;
|
|
78
|
+
none: () => N2;
|
|
79
|
+
}>,
|
|
80
|
+
]
|
|
81
|
+
): N2 | S2 | ((optional: O) => N2 | S2) {
|
|
82
|
+
switch (args.length) {
|
|
83
|
+
case 2: {
|
|
84
|
+
const [optional, cases] = args;
|
|
85
|
+
|
|
86
|
+
return matchImpl(optional, cases);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
case 1: {
|
|
90
|
+
// Curried version: the only argument is the case handlers
|
|
91
|
+
const [cases] = args;
|
|
92
|
+
|
|
93
|
+
return (optional: O) => matchImpl(optional, cases);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const matchImpl = <O extends UnknownOptional, S2, N2>(
|
|
99
|
+
optional: O,
|
|
100
|
+
cases: Readonly<{
|
|
101
|
+
some: (value: Unwrap<O>) => S2;
|
|
102
|
+
none: () => N2;
|
|
103
|
+
}>,
|
|
104
|
+
): N2 | S2 =>
|
|
105
|
+
isSome(optional)
|
|
106
|
+
? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
107
|
+
cases.some(unwrap(optional)!)
|
|
108
|
+
: cases.none();
|
|
@@ -800,4 +800,83 @@ describe('Optional test', () => {
|
|
|
800
800
|
expect(Optional.unwrap(inner)).toBe(42);
|
|
801
801
|
});
|
|
802
802
|
});
|
|
803
|
+
|
|
804
|
+
describe('match', () => {
|
|
805
|
+
test('should apply the some case for Some', () => {
|
|
806
|
+
const result = Optional.match(Optional.some(21), {
|
|
807
|
+
some: (value) => value * 2,
|
|
808
|
+
none: () => 0,
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
expectType<typeof result, number>('=');
|
|
812
|
+
|
|
813
|
+
assert.deepStrictEqual(result, 42);
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
test('should apply the none case for None', () => {
|
|
817
|
+
const result = Optional.match(Optional.none, {
|
|
818
|
+
some: () => 'some',
|
|
819
|
+
none: () => 'none',
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
assert.deepStrictEqual(result, 'none');
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
test('should support Optional union inputs', () => {
|
|
826
|
+
const optional: Optional<number> = Optional.some(3);
|
|
827
|
+
|
|
828
|
+
const result = Optional.match(optional, {
|
|
829
|
+
some: (value) => `value: ${value}`,
|
|
830
|
+
none: () => 'none',
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
expectType<typeof result, string>('=');
|
|
834
|
+
|
|
835
|
+
assert.deepStrictEqual(result, 'value: 3');
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
test('curried version should work', () => {
|
|
839
|
+
const matcher = Optional.match({
|
|
840
|
+
some: (value: number) => `value: ${value}`,
|
|
841
|
+
none: () => 'none',
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
const result = matcher(Optional.some(3));
|
|
845
|
+
|
|
846
|
+
expectType<typeof result, string>('=');
|
|
847
|
+
|
|
848
|
+
assert.deepStrictEqual(result, 'value: 3');
|
|
849
|
+
|
|
850
|
+
assert.deepStrictEqual(matcher(Optional.none), 'none');
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
test('curried version should compose with Array.map and pipe', () => {
|
|
854
|
+
const matcher = Optional.match({
|
|
855
|
+
some: (value: number) => `some:${value}`,
|
|
856
|
+
none: () => 'none',
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
const optionals: readonly Optional<number>[] = [
|
|
860
|
+
Optional.some(1),
|
|
861
|
+
Optional.none,
|
|
862
|
+
] as const;
|
|
863
|
+
|
|
864
|
+
assert.deepStrictEqual(optionals.map(matcher), ['some:1', 'none']);
|
|
865
|
+
|
|
866
|
+
const single: Optional<number> = Optional.some(3);
|
|
867
|
+
|
|
868
|
+
assert.deepStrictEqual(pipe(single).map(matcher).value, 'some:3');
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
test('should apply the some case for Some(undefined)', () => {
|
|
872
|
+
const makeOptional = (): Optional<undefined> => Optional.some(undefined);
|
|
873
|
+
|
|
874
|
+
const result = Optional.match(makeOptional(), {
|
|
875
|
+
some: (value) => `some: ${String(value)}`,
|
|
876
|
+
none: () => 'none',
|
|
877
|
+
});
|
|
878
|
+
|
|
879
|
+
assert.deepStrictEqual(result, 'some: undefined');
|
|
880
|
+
});
|
|
881
|
+
});
|
|
803
882
|
});
|
|
@@ -2,6 +2,7 @@ export * from './result-err.mjs';
|
|
|
2
2
|
export * from './result-expect-to-be.mjs';
|
|
3
3
|
export * from './result-flat-map.mjs';
|
|
4
4
|
export * from './result-fold.mjs';
|
|
5
|
+
export * from './result-from-optional.mjs';
|
|
5
6
|
export * from './result-from-promise.mjs';
|
|
6
7
|
export * from './result-from-throwable.mjs';
|
|
7
8
|
export * from './result-is-err.mjs';
|
|
@@ -9,8 +10,11 @@ export * from './result-is-ok.mjs';
|
|
|
9
10
|
export * from './result-is-result.mjs';
|
|
10
11
|
export * from './result-map-err.mjs';
|
|
11
12
|
export * from './result-map.mjs';
|
|
13
|
+
export * from './result-match.mjs';
|
|
12
14
|
export * from './result-ok.mjs';
|
|
13
15
|
export * from './result-or-else.mjs';
|
|
16
|
+
export * from './result-safe-try.mjs';
|
|
17
|
+
export * from './result-safe-unwrap.mjs';
|
|
14
18
|
export * from './result-swap.mjs';
|
|
15
19
|
export * from './result-to-optional.mjs';
|
|
16
20
|
export * from './result-unwrap-err-or.mjs';
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Optional, type UnknownOptional } from '../../optional/index.mjs';
|
|
2
|
+
import { type Result } from '../result.mjs';
|
|
3
|
+
import { err } from './result-err.mjs';
|
|
4
|
+
import { ok } from './result-ok.mjs';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Converts an {@link Optional} to a `Result` (the inverse of
|
|
8
|
+
* {@link Result.toOptional}, and the equivalent of Rust's `Option::ok_or`).
|
|
9
|
+
*
|
|
10
|
+
* If the {@link Optional} is `Some`, returns `Ok` with the contained value.
|
|
11
|
+
* If it is `None`, returns `Err` with the provided error value.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* assert.deepStrictEqual(
|
|
17
|
+
* Result.fromOptional(Optional.some(42), 'missing'),
|
|
18
|
+
* Result.ok(42),
|
|
19
|
+
* );
|
|
20
|
+
*
|
|
21
|
+
* assert.deepStrictEqual(
|
|
22
|
+
* Result.fromOptional(Optional.none, 'missing'),
|
|
23
|
+
* Result.err('missing'),
|
|
24
|
+
* );
|
|
25
|
+
*
|
|
26
|
+
* const withMissingError = Result.fromOptional('missing');
|
|
27
|
+
*
|
|
28
|
+
* assert.deepStrictEqual(withMissingError(Optional.some(1)), Result.ok(1));
|
|
29
|
+
*
|
|
30
|
+
* assert.deepStrictEqual(
|
|
31
|
+
* withMissingError(Optional.none),
|
|
32
|
+
* Result.err('missing'),
|
|
33
|
+
* );
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @template O The input `UnknownOptional` type.
|
|
37
|
+
* @template E The type of the error value used for the `None` case.
|
|
38
|
+
* @param optional The `Optional` to convert.
|
|
39
|
+
* @param error The error value to use if the `Optional` is `None`.
|
|
40
|
+
* @returns `Ok` with the contained value, or `Err` with `error`.
|
|
41
|
+
*/
|
|
42
|
+
export function fromOptional<O extends UnknownOptional, const E>(
|
|
43
|
+
optional: O,
|
|
44
|
+
error: E,
|
|
45
|
+
): Result<Optional.Unwrap<O>, E>;
|
|
46
|
+
|
|
47
|
+
// Curried version
|
|
48
|
+
export function fromOptional<const E>(
|
|
49
|
+
error: E,
|
|
50
|
+
): <O extends UnknownOptional>(optional: O) => Result<Optional.Unwrap<O>, E>;
|
|
51
|
+
|
|
52
|
+
export function fromOptional<O extends UnknownOptional, const E>(
|
|
53
|
+
...args: readonly [optional: O, error: E] | readonly [error: E]
|
|
54
|
+
):
|
|
55
|
+
| Result<Optional.Unwrap<O>, E>
|
|
56
|
+
| ((optional: O) => Result<Optional.Unwrap<O>, E>) {
|
|
57
|
+
switch (args.length) {
|
|
58
|
+
case 2: {
|
|
59
|
+
const [optional, error] = args;
|
|
60
|
+
|
|
61
|
+
return fromOptionalImpl(optional, error);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
case 1: {
|
|
65
|
+
// Curried version: the only argument is the error value
|
|
66
|
+
const [error] = args;
|
|
67
|
+
|
|
68
|
+
return (optional: O) => fromOptionalImpl(optional, error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const fromOptionalImpl = <O extends UnknownOptional, E>(
|
|
74
|
+
optional: O,
|
|
75
|
+
error: E,
|
|
76
|
+
): Result<Optional.Unwrap<O>, E> =>
|
|
77
|
+
Optional.isSome(optional)
|
|
78
|
+
? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
79
|
+
ok(Optional.unwrap(optional)!)
|
|
80
|
+
: err(error);
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { type UnknownResult } from '../result.mjs';
|
|
2
|
+
import { isOk } from './result-is-ok.mjs';
|
|
3
|
+
import { type UnwrapErr, type UnwrapOk } from './types.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Folds a `Result` into a plain value by applying one of two case handlers:
|
|
7
|
+
* `ok` if the `Result` is `Ok`, `err` otherwise. This is the function form of
|
|
8
|
+
* pattern matching on a `Result`.
|
|
9
|
+
*
|
|
10
|
+
* Unlike {@link Result.fold}, which maps both sides and returns another
|
|
11
|
+
* `Result`, `match` unwraps: both branches produce the final value directly.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* const doubledOrZero = Result.match(Result.ok(21), {
|
|
17
|
+
* ok: (value) => value * 2,
|
|
18
|
+
* err: () => 0,
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* assert.deepStrictEqual(doubledOrZero, 42);
|
|
22
|
+
*
|
|
23
|
+
* const matcher = Result.match({
|
|
24
|
+
* ok: (value: number) => value + 1,
|
|
25
|
+
* err: (error: string) => error.length,
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* assert.deepStrictEqual(matcher(Result.ok(1)), 2);
|
|
29
|
+
*
|
|
30
|
+
* assert.deepStrictEqual(matcher(Result.err('oops')), 4);
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @template R The input `UnknownResult` type.
|
|
34
|
+
* @template S2 The type returned by the `ok` case.
|
|
35
|
+
* @template E2 The type returned by the `err` case.
|
|
36
|
+
* @param result The `Result` to match on.
|
|
37
|
+
* @param cases An object with an `ok` handler receiving the success value and
|
|
38
|
+
* an `err` handler receiving the error value.
|
|
39
|
+
* @returns The value produced by whichever case handler was applied.
|
|
40
|
+
*/
|
|
41
|
+
export function match<R extends UnknownResult, S2, E2>(
|
|
42
|
+
result: R,
|
|
43
|
+
cases: Readonly<{
|
|
44
|
+
ok: (value: UnwrapOk<R>) => S2;
|
|
45
|
+
err: (error: UnwrapErr<R>) => E2;
|
|
46
|
+
}>,
|
|
47
|
+
): E2 | S2;
|
|
48
|
+
|
|
49
|
+
// Curried version
|
|
50
|
+
//
|
|
51
|
+
// The returned function is generic over the *whole* result, mirroring the
|
|
52
|
+
// direct overload above; see the comment on the curried overload of
|
|
53
|
+
// `Result.map` for why the parameter is a conditional type instead of a plain
|
|
54
|
+
// `Result<S, E>`. Both sides have to be checked here, hence the nested
|
|
55
|
+
// conditional.
|
|
56
|
+
export function match<S, E, S2, E2>(
|
|
57
|
+
cases: Readonly<{
|
|
58
|
+
ok: (value: S) => S2;
|
|
59
|
+
err: (error: E) => E2;
|
|
60
|
+
}>,
|
|
61
|
+
): <R extends UnknownResult>(
|
|
62
|
+
result: UnwrapOk<R> extends S ? (UnwrapErr<R> extends E ? R : never) : never,
|
|
63
|
+
) => E2 | S2;
|
|
64
|
+
|
|
65
|
+
export function match<R extends UnknownResult, S2, E2>(
|
|
66
|
+
...args:
|
|
67
|
+
| readonly [
|
|
68
|
+
result: R,
|
|
69
|
+
cases: Readonly<{
|
|
70
|
+
ok: (value: UnwrapOk<R>) => S2;
|
|
71
|
+
err: (error: UnwrapErr<R>) => E2;
|
|
72
|
+
}>,
|
|
73
|
+
]
|
|
74
|
+
| readonly [
|
|
75
|
+
cases: Readonly<{
|
|
76
|
+
ok: (value: UnwrapOk<R>) => S2;
|
|
77
|
+
err: (error: UnwrapErr<R>) => E2;
|
|
78
|
+
}>,
|
|
79
|
+
]
|
|
80
|
+
): E2 | S2 | ((result: R) => E2 | S2) {
|
|
81
|
+
switch (args.length) {
|
|
82
|
+
case 2: {
|
|
83
|
+
const [result, cases] = args;
|
|
84
|
+
|
|
85
|
+
return matchImpl(result, cases);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
case 1: {
|
|
89
|
+
// Curried version: the only argument is the case handlers
|
|
90
|
+
const [cases] = args;
|
|
91
|
+
|
|
92
|
+
return (result: R) => matchImpl(result, cases);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const matchImpl = <R extends UnknownResult, S2, E2>(
|
|
98
|
+
result: R,
|
|
99
|
+
cases: Readonly<{
|
|
100
|
+
ok: (value: UnwrapOk<R>) => S2;
|
|
101
|
+
err: (error: UnwrapErr<R>) => E2;
|
|
102
|
+
}>,
|
|
103
|
+
): E2 | S2 =>
|
|
104
|
+
isOk(result)
|
|
105
|
+
? // eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
106
|
+
cases.ok(result.value as UnwrapOk<R>)
|
|
107
|
+
: // eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
108
|
+
cases.err(result.value as UnwrapErr<R>);
|