ts-data-forge 7.1.0 → 8.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/dist/array/impl/array-utils-creation.d.mts +2 -2
- package/dist/array/impl/array-utils-creation.d.mts.map +1 -1
- package/dist/functional/optional/impl/optional-expect-to-be.d.mts +2 -2
- package/dist/functional/optional/impl/optional-expect-to-be.d.mts.map +1 -1
- package/dist/functional/optional/impl/optional-or-else.d.mts +1 -1
- package/dist/functional/optional/impl/optional-or-else.d.mts.map +1 -1
- package/dist/functional/optional/impl/optional-unwrap-or.d.mts +1 -1
- package/dist/functional/optional/impl/optional-unwrap-or.d.mts.map +1 -1
- package/dist/functional/result/impl/result-expect-to-be.d.mts +3 -3
- package/dist/functional/result/impl/result-expect-to-be.d.mts.map +1 -1
- package/dist/functional/result/impl/result-expect-to-be.mjs.map +1 -1
- package/dist/functional/result/impl/result-or-else.d.mts +2 -2
- package/dist/functional/result/impl/result-or-else.d.mts.map +1 -1
- package/dist/functional/result/impl/result-unwrap-err-or.d.mts +2 -2
- package/dist/functional/result/impl/result-unwrap-err-or.d.mts.map +1 -1
- package/dist/functional/result/impl/result-unwrap-ok-or.d.mts +2 -2
- package/dist/functional/result/impl/result-unwrap-ok-or.d.mts.map +1 -1
- package/dist/functional/ternary-result/impl/ternary-result-expect-to-be.d.mts +2 -2
- package/dist/functional/ternary-result/impl/ternary-result-expect-to-be.d.mts.map +1 -1
- package/dist/functional/ternary-result/impl/ternary-result-or-else.d.mts +2 -2
- package/dist/functional/ternary-result/impl/ternary-result-or-else.d.mts.map +1 -1
- package/dist/functional/ternary-result/impl/ternary-result-or-else.mjs.map +1 -1
- package/dist/functional/ternary-result/impl/ternary-result-unwrap-err-or.d.mts +2 -2
- package/dist/functional/ternary-result/impl/ternary-result-unwrap-err-or.d.mts.map +1 -1
- package/dist/functional/ternary-result/impl/ternary-result-unwrap-err-or.mjs.map +1 -1
- package/dist/functional/ternary-result/impl/ternary-result-unwrap-ok-or.d.mts +2 -2
- package/dist/functional/ternary-result/impl/ternary-result-unwrap-ok-or.d.mts.map +1 -1
- package/dist/functional/ternary-result/impl/ternary-result-unwrap-ok-or.mjs.map +1 -1
- package/dist/functional/ternary-result/impl/ternary-result-unwrap-warn-or.d.mts +2 -2
- package/dist/functional/ternary-result/impl/ternary-result-unwrap-warn-or.d.mts.map +1 -1
- package/dist/functional/ternary-result/impl/ternary-result-unwrap-warn-or.mjs.map +1 -1
- package/dist/guard/is-non-null-object.d.mts +3 -3
- package/dist/guard/is-non-null-object.mjs +3 -3
- package/dist/guard/is-record.d.mts +47 -29
- package/dist/guard/is-record.d.mts.map +1 -1
- package/dist/guard/is-record.mjs +48 -30
- package/dist/guard/is-record.mjs.map +1 -1
- package/dist/others/fast-deep-equal.d.mts.map +1 -1
- package/dist/others/fast-deep-equal.mjs +7 -5
- package/dist/others/fast-deep-equal.mjs.map +1 -1
- package/package.json +4 -4
- package/src/array/impl/array-utils-creation.mts +1 -1
- package/src/array/impl/array-utils-transformation.mts +1 -1
- package/src/functional/optional/impl/optional-expect-to-be.mts +2 -2
- package/src/functional/optional/impl/optional-or-else.mts +3 -3
- package/src/functional/optional/impl/optional-unwrap-or.mts +2 -2
- package/src/functional/optional.test.mts +3 -3
- package/src/functional/result/impl/result-expect-to-be.mts +5 -3
- package/src/functional/result/impl/result-or-else.mts +4 -4
- package/src/functional/result/impl/result-unwrap-err-or.mts +2 -2
- package/src/functional/result/impl/result-unwrap-ok-or.mts +3 -3
- package/src/functional/ternary-result/impl/ternary-result-expect-to-be.mts +3 -3
- package/src/functional/ternary-result/impl/ternary-result-or-else.mts +6 -9
- package/src/functional/ternary-result/impl/ternary-result-unwrap-err-or.mts +3 -6
- package/src/functional/ternary-result/impl/ternary-result-unwrap-ok-or.mts +3 -6
- package/src/functional/ternary-result/impl/ternary-result-unwrap-warn-or.mts +3 -6
- package/src/guard/is-non-null-object.mts +3 -3
- package/src/guard/is-record.mts +49 -30
- package/src/guard/is-record.test.mts +133 -61
- package/src/others/fast-deep-equal.mts +10 -8
package/dist/guard/is-record.mjs
CHANGED
|
@@ -1,31 +1,46 @@
|
|
|
1
1
|
import { isNonNullObject } from './is-non-null-object.mjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Type guard that checks if a value is a
|
|
5
|
-
* object
|
|
4
|
+
* Type guard that checks if a value is a record — a non-null, non-array
|
|
5
|
+
* object — and narrows it to `UnknownRecord` (= `ReadonlyRecord<string,
|
|
6
|
+
* unknown>`).
|
|
6
7
|
*
|
|
7
|
-
* This function is
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* types like Date, RegExp, etc., focusing on objects that can be safely treated
|
|
11
|
-
* as property collections.
|
|
8
|
+
* This function is designed to be combined with {@link hasKey} to probe
|
|
9
|
+
* values of type `unknown` (e.g. parsed JSON, caught errors, external data)
|
|
10
|
+
* in a type-safe way: `if (isRecord(u) && hasKey(u, 'some-key')) { ... }`.
|
|
12
11
|
*
|
|
13
12
|
* **Type Narrowing Behavior:**
|
|
14
13
|
*
|
|
15
|
-
* - Narrows `unknown` to `UnknownRecord` (
|
|
16
|
-
*
|
|
17
|
-
* - Excludes
|
|
18
|
-
*
|
|
19
|
-
* with
|
|
20
|
-
* - Returns `
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
14
|
+
* - Narrows `unknown` to `UnknownRecord` (= `ReadonlyRecord<string, unknown>`)
|
|
15
|
+
* - Excludes `null`, `undefined`, primitives, and functions
|
|
16
|
+
* - Excludes arrays: arrays have no string index signature, so they are not
|
|
17
|
+
* assignable to `UnknownRecord`; admitting them at runtime would be
|
|
18
|
+
* inconsistent with the narrowed type
|
|
19
|
+
* - Returns `true` for every other non-null object, including dictionary
|
|
20
|
+
* objects created with `Object.create(null)` and instances such as `Date`,
|
|
21
|
+
* `RegExp`, `Map`, `Set`, `Error`, and user-defined classes
|
|
22
|
+
*
|
|
23
|
+
* **Why non-plain objects are included:** since every property of
|
|
24
|
+
* `UnknownRecord` is typed `unknown`, reading properties from any object
|
|
25
|
+
* through this narrowing is type-safe, and `hasKey` (backed by
|
|
26
|
+
* `Object.hasOwn`) answers own-property questions correctly on any object
|
|
27
|
+
* (e.g. `Map` entries are not own properties, so `hasKey` correctly returns
|
|
28
|
+
* `false` for them). Restricting the guard to plain objects would require
|
|
29
|
+
* prototype checks that misjudge cross-realm objects and would prevent
|
|
30
|
+
* probing useful instances such as `Error` subclasses.
|
|
31
|
+
*
|
|
32
|
+
* **Implementation:** `isNonNullObject(u) && !Array.isArray(u)`.
|
|
24
33
|
*
|
|
25
34
|
* @example
|
|
26
35
|
*
|
|
27
36
|
* ```ts
|
|
28
|
-
* const entries: readonly unknown[] = [
|
|
37
|
+
* const entries: readonly unknown[] = [
|
|
38
|
+
* { id: 1 },
|
|
39
|
+
* [1, 2],
|
|
40
|
+
* 'str',
|
|
41
|
+
* 0,
|
|
42
|
+
* null,
|
|
43
|
+
* ] as const;
|
|
29
44
|
*
|
|
30
45
|
* const records = entries.filter(isRecord);
|
|
31
46
|
*
|
|
@@ -33,29 +48,31 @@ import { isNonNullObject } from './is-non-null-object.mjs';
|
|
|
33
48
|
* ```
|
|
34
49
|
*
|
|
35
50
|
* @param u - The value to check
|
|
36
|
-
* @returns `true` if `u` is a non-null object
|
|
37
|
-
*
|
|
38
|
-
* @see {@link isNonNullObject} - For checking any object
|
|
51
|
+
* @returns `true` if `u` is a non-null, non-array object, `false` otherwise.
|
|
52
|
+
* When `true`, TypeScript narrows the type to `UnknownRecord`.
|
|
53
|
+
* @see {@link isNonNullObject} - For checking any non-null object (includes arrays)
|
|
39
54
|
* @see {@link hasKey} - For checking if a record has specific keys
|
|
40
55
|
*/
|
|
41
|
-
const isRecord = (u) => isNonNullObject(u);
|
|
56
|
+
const isRecord = (u) => isNonNullObject(u) && !Array.isArray(u);
|
|
42
57
|
/**
|
|
43
|
-
* Type guard that checks if a value is a mutable record
|
|
58
|
+
* Type guard that checks if a value is a mutable record.
|
|
44
59
|
*
|
|
45
|
-
* This function
|
|
46
|
-
*
|
|
47
|
-
*
|
|
60
|
+
* This function has the same runtime behavior as {@link isRecord} (non-null,
|
|
61
|
+
* non-array object check), but narrows the type to `MutableRecord<string,
|
|
62
|
+
* unknown>`, which is useful when you need to add or overwrite properties on
|
|
63
|
+
* the narrowed object.
|
|
48
64
|
*
|
|
49
65
|
* **Type Narrowing Behavior:**
|
|
50
66
|
*
|
|
51
67
|
* - Narrows `unknown` to `MutableRecord<string, unknown>`
|
|
52
|
-
* - Excludes `null`, `undefined`, primitives,
|
|
53
|
-
* - Returns `true` for
|
|
54
|
-
* -
|
|
68
|
+
* - Excludes `null`, `undefined`, primitives, functions, and arrays
|
|
69
|
+
* - Returns `true` for every other non-null object (see {@link isRecord} for
|
|
70
|
+
* details on non-plain objects such as `Date`, `Map`, and `Set`)
|
|
55
71
|
*
|
|
56
72
|
* **Implementation:** This is a type alias for {@link isRecord}.
|
|
57
73
|
*
|
|
58
74
|
* @example
|
|
75
|
+
*
|
|
59
76
|
* ```ts
|
|
60
77
|
* const obj: unknown = { foo: 1 } as const;
|
|
61
78
|
*
|
|
@@ -65,8 +82,9 @@ const isRecord = (u) => isNonNullObject(u);
|
|
|
65
82
|
* ```
|
|
66
83
|
*
|
|
67
84
|
* @param u - The value to check
|
|
68
|
-
* @returns `true` if `u` is a
|
|
69
|
-
* When `true`, TypeScript narrows the type to `MutableRecord<string,
|
|
85
|
+
* @returns `true` if `u` is a non-null, non-array object, `false` otherwise.
|
|
86
|
+
* When `true`, TypeScript narrows the type to `MutableRecord<string,
|
|
87
|
+
* unknown>`.
|
|
70
88
|
* @see {@link isRecord} - For the underlying implementation and more details
|
|
71
89
|
*/
|
|
72
90
|
const isMutableRecord = isRecord;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"is-record.mjs","sources":["../../src/guard/is-record.mts"],"sourcesContent":[null],"names":[],"mappings":";;AAGA
|
|
1
|
+
{"version":3,"file":"is-record.mjs","sources":["../../src/guard/is-record.mts"],"sourcesContent":[null],"names":[],"mappings":";;AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDG;MACU,QAAQ,GAAG,CAAC,CAAU,KACjC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCG;AACI,MAAM,eAAe,GAEe;;;;"}
|
|
@@ -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,EAAG,GAAG,CAAC,EAAE,GAAG,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,EAAG,GAAG,CAAC,EAAE,GAAG,CAAC,KAAG,OAuF9C,CAAC"}
|
|
@@ -15,6 +15,13 @@ const fastDeepEqual = (a, b) => {
|
|
|
15
15
|
if (typeof a !== typeof b) {
|
|
16
16
|
return false;
|
|
17
17
|
}
|
|
18
|
+
// Arrays are handled before `isRecord`, which excludes them.
|
|
19
|
+
if (Array.isArray(a)) {
|
|
20
|
+
return (Array.isArray(b) &&
|
|
21
|
+
a.constructor === b.constructor &&
|
|
22
|
+
a.length === b.length &&
|
|
23
|
+
a.every((ai, index) => fastDeepEqual(ai, b[index])));
|
|
24
|
+
}
|
|
18
25
|
if (isRecord(a)) {
|
|
19
26
|
if (!isRecord(b)) {
|
|
20
27
|
return false;
|
|
@@ -22,11 +29,6 @@ const fastDeepEqual = (a, b) => {
|
|
|
22
29
|
if (a.constructor !== b.constructor) {
|
|
23
30
|
return false;
|
|
24
31
|
}
|
|
25
|
-
if (Array.isArray(a)) {
|
|
26
|
-
return (Array.isArray(b) &&
|
|
27
|
-
a.length === b.length &&
|
|
28
|
-
a.every((ai, index) => fastDeepEqual(ai, b[index])));
|
|
29
|
-
}
|
|
30
32
|
if (isMap(a)) {
|
|
31
33
|
return (isMap(b) &&
|
|
32
34
|
a.size === b.size &&
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fast-deep-equal.mjs","sources":["../../src/others/fast-deep-equal.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;AAGA;;;;;AAKG;MACU,aAAa,GAAG,CAAK,CAAI,EAAE,CAAI,KAAa;AACvD,IAAA,IAAI,CAAC,KAAK,CAAC,EAAE;AACX,QAAA,OAAO,IAAI;IACb;AAEA,IAAA,IAAI,OAAO,CAAC,KAAK,OAAO,CAAC,EAAE;AACzB,QAAA,OAAO,KAAK;IACd
|
|
1
|
+
{"version":3,"file":"fast-deep-equal.mjs","sources":["../../src/others/fast-deep-equal.mts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-data-forge",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
"devDependencies": {
|
|
92
92
|
"@emotion/react": "11.14.0",
|
|
93
93
|
"@emotion/styled": "11.14.1",
|
|
94
|
-
"@mui/material": "9.1.
|
|
94
|
+
"@mui/material": "9.1.2",
|
|
95
95
|
"@rollup/plugin-replace": "6.0.3",
|
|
96
96
|
"@rollup/plugin-strip": "3.0.4",
|
|
97
97
|
"@rollup/plugin-typescript": "12.3.0",
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"@vitest/browser-playwright": "4.1.9",
|
|
108
108
|
"@vitest/coverage-v8": "4.1.9",
|
|
109
109
|
"@vitest/ui": "4.1.9",
|
|
110
|
-
"conventional-changelog-conventionalcommits": "
|
|
110
|
+
"conventional-changelog-conventionalcommits": "10.2.0",
|
|
111
111
|
"cspell": "10.0.1",
|
|
112
112
|
"dedent": "1.7.2",
|
|
113
113
|
"eslint": "9.39.4",
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
"typedoc": "0.28.19",
|
|
133
133
|
"typedoc-github-theme": "0.4.0",
|
|
134
134
|
"typescript": "5.9.3",
|
|
135
|
-
"vite": "8.0
|
|
135
|
+
"vite": "8.1.0",
|
|
136
136
|
"vitest": "4.1.9"
|
|
137
137
|
},
|
|
138
138
|
"peerDependencies": {
|
|
@@ -85,7 +85,7 @@ export const seq = <N extends SizeType.ArgArr>(
|
|
|
85
85
|
* assert.deepStrictEqual(emptyStrings, []);
|
|
86
86
|
* ```
|
|
87
87
|
*/
|
|
88
|
-
export const create = <
|
|
88
|
+
export const create = <N extends SizeType.ArgArr, const V>(
|
|
89
89
|
len: N,
|
|
90
90
|
init: V,
|
|
91
91
|
): N extends SmallUint
|
|
@@ -143,7 +143,7 @@ export function scan<E, S>(
|
|
|
143
143
|
const [array, reducer, init] = args;
|
|
144
144
|
|
|
145
145
|
const mut_result: MutableNonEmptyArray<S> = castMutable(
|
|
146
|
-
newArray<
|
|
146
|
+
newArray<PositiveUint32, S>(asPositiveUint32(array.length + 1), init),
|
|
147
147
|
);
|
|
148
148
|
|
|
149
149
|
let mut_acc = init;
|
|
@@ -16,7 +16,7 @@ import { type Unwrap } from './types.mjs';
|
|
|
16
16
|
* Optional.expectToBe(optionalValue, 'value expected') === 'data',
|
|
17
17
|
* );
|
|
18
18
|
*
|
|
19
|
-
* const expectValue = Optional.expectToBe
|
|
19
|
+
* const expectValue = Optional.expectToBe('missing optional');
|
|
20
20
|
*
|
|
21
21
|
* const throwTest = (): void => {
|
|
22
22
|
* expectValue(Optional.none);
|
|
@@ -41,7 +41,7 @@ export function expectToBe<O extends UnknownOptional>(
|
|
|
41
41
|
): Unwrap<O>;
|
|
42
42
|
|
|
43
43
|
// Curried version
|
|
44
|
-
export function expectToBe
|
|
44
|
+
export function expectToBe(message: string): <O>(optional: Optional<O>) => O;
|
|
45
45
|
|
|
46
46
|
export function expectToBe<O extends UnknownOptional>(
|
|
47
47
|
...args: readonly [optional: O, message: string] | readonly [message: string]
|
|
@@ -47,9 +47,9 @@ export function orElse<
|
|
|
47
47
|
>(optional: O, alternative: O2): O | O2;
|
|
48
48
|
|
|
49
49
|
// Curried version
|
|
50
|
-
export function orElse<
|
|
51
|
-
alternative: Optional<
|
|
52
|
-
): (optional: Optional<
|
|
50
|
+
export function orElse<O2>(
|
|
51
|
+
alternative: Optional<O2>,
|
|
52
|
+
): <O>(optional: Optional<O>) => Optional<O> | Optional<O2>;
|
|
53
53
|
|
|
54
54
|
export function orElse<
|
|
55
55
|
O extends UnknownOptional,
|
|
@@ -40,9 +40,9 @@ export function unwrapOr<O extends UnknownOptional, D>(
|
|
|
40
40
|
): D | Unwrap<O>;
|
|
41
41
|
|
|
42
42
|
// Curried version
|
|
43
|
-
export function unwrapOr<
|
|
43
|
+
export function unwrapOr<D>(
|
|
44
44
|
defaultValue: D,
|
|
45
|
-
): (optional: Optional<
|
|
45
|
+
): <O>(optional: Optional<O>) => D | O;
|
|
46
46
|
|
|
47
47
|
export function unwrapOr<O extends UnknownOptional, D>(
|
|
48
48
|
...args: readonly [optional: O, defaultValue: D] | readonly [defaultValue: D]
|
|
@@ -295,19 +295,19 @@ describe('Optional test', () => {
|
|
|
295
295
|
|
|
296
296
|
describe('expectToBe', () => {
|
|
297
297
|
test('should return the value for Some', () => {
|
|
298
|
-
const expectNumber = Optional.expectToBe
|
|
298
|
+
const expectNumber = Optional.expectToBe('Expected a number');
|
|
299
299
|
|
|
300
300
|
expect(expectNumber(Optional.some(42))).toBe(42);
|
|
301
301
|
});
|
|
302
302
|
|
|
303
303
|
test('should throw with custom message for None', () => {
|
|
304
|
-
const expectNumber = Optional.expectToBe
|
|
304
|
+
const expectNumber = Optional.expectToBe('Expected a number');
|
|
305
305
|
|
|
306
306
|
expect(() => expectNumber(Optional.none)).toThrow('Expected a number');
|
|
307
307
|
});
|
|
308
308
|
|
|
309
309
|
test('should be curried', () => {
|
|
310
|
-
const expectValidId = Optional.expectToBe
|
|
310
|
+
const expectValidId = Optional.expectToBe('ID is required');
|
|
311
311
|
|
|
312
312
|
const id1 = Optional.some('user-123');
|
|
313
313
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type UnknownResult } from '../result.mjs';
|
|
2
2
|
import { isOk } from './result-is-ok.mjs';
|
|
3
3
|
import { unwrapOk } from './result-unwrap-ok.mjs';
|
|
4
4
|
import { type UnwrapOk } from './types.mjs';
|
|
@@ -14,7 +14,7 @@ import { type UnwrapOk } from './types.mjs';
|
|
|
14
14
|
*
|
|
15
15
|
* assert.isTrue(Result.expectToBe(okValue, 'should have value') === 'data');
|
|
16
16
|
*
|
|
17
|
-
* const expectResult = Result.expectToBe
|
|
17
|
+
* const expectResult = Result.expectToBe('missing result');
|
|
18
18
|
*
|
|
19
19
|
* const throwTest = (): void => {
|
|
20
20
|
* expectResult(Result.err('boom'));
|
|
@@ -37,7 +37,9 @@ export function expectToBe<R extends UnknownResult>(
|
|
|
37
37
|
): UnwrapOk<R>;
|
|
38
38
|
|
|
39
39
|
// Curried version
|
|
40
|
-
export function expectToBe
|
|
40
|
+
export function expectToBe(
|
|
41
|
+
message: string,
|
|
42
|
+
): <R extends UnknownResult>(result: R) => UnwrapOk<R>;
|
|
41
43
|
|
|
42
44
|
export function expectToBe<R extends UnknownResult>(
|
|
43
45
|
...args: readonly [result: R, message: string] | readonly [message: string]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type UnknownResult } from '../result.mjs';
|
|
2
2
|
import { isOk } from './result-is-ok.mjs';
|
|
3
3
|
import { type NarrowToOk } from './types.mjs';
|
|
4
4
|
|
|
@@ -43,9 +43,9 @@ export function orElse<R extends UnknownResult, R2 extends UnknownResult>(
|
|
|
43
43
|
): NarrowToOk<R> | R2;
|
|
44
44
|
|
|
45
45
|
// Curried version
|
|
46
|
-
export function orElse<
|
|
47
|
-
alternative:
|
|
48
|
-
): (result:
|
|
46
|
+
export function orElse<R2 extends UnknownResult>(
|
|
47
|
+
alternative: R2,
|
|
48
|
+
): <R extends UnknownResult>(result: R) => NarrowToOk<R> | R2;
|
|
49
49
|
|
|
50
50
|
export function orElse<R extends UnknownResult, R2 extends UnknownResult>(
|
|
51
51
|
...args: readonly [result: R, alternative: R2] | readonly [alternative: R2]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type UnknownResult } from '../result.mjs';
|
|
2
2
|
import { unwrapErr } from './result-unwrap-err.mjs';
|
|
3
3
|
import { type UnwrapErr } from './types.mjs';
|
|
4
4
|
|
|
@@ -38,7 +38,7 @@ export function unwrapErrOr<R extends UnknownResult, D>(
|
|
|
38
38
|
// Curried version
|
|
39
39
|
export function unwrapErrOr<D>(
|
|
40
40
|
defaultValue: D,
|
|
41
|
-
): <
|
|
41
|
+
): <R extends UnknownResult>(result: R) => D | UnwrapErr<R>;
|
|
42
42
|
|
|
43
43
|
export function unwrapErrOr<R extends UnknownResult, D>(
|
|
44
44
|
...args: readonly [result: R, defaultValue: D] | readonly [defaultValue: D]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type UnknownResult } from '../result.mjs';
|
|
2
2
|
import { unwrapOk } from './result-unwrap-ok.mjs';
|
|
3
3
|
import { type UnwrapOk } from './types.mjs';
|
|
4
4
|
|
|
@@ -36,9 +36,9 @@ export function unwrapOkOr<R extends UnknownResult, D>(
|
|
|
36
36
|
): D | UnwrapOk<R>;
|
|
37
37
|
|
|
38
38
|
// Curried version
|
|
39
|
-
export function unwrapOkOr<
|
|
39
|
+
export function unwrapOkOr<D>(
|
|
40
40
|
defaultValue: D,
|
|
41
|
-
): <
|
|
41
|
+
): <R extends UnknownResult>(result: R) => D | UnwrapOk<R>;
|
|
42
42
|
|
|
43
43
|
export function unwrapOkOr<R extends UnknownResult, D>(
|
|
44
44
|
...args: readonly [result: R, defaultValue: D] | readonly [defaultValue: D]
|
|
@@ -16,7 +16,7 @@ import { type UnwrapOk } from './types.mjs';
|
|
|
16
16
|
*
|
|
17
17
|
* assert.strictEqual(TernaryResult.expectToBe(okValue, 'missing'), 'ready');
|
|
18
18
|
*
|
|
19
|
-
* const expectResult = TernaryResult.expectToBe
|
|
19
|
+
* const expectResult = TernaryResult.expectToBe('needs value');
|
|
20
20
|
*
|
|
21
21
|
* const throwTest = (): void => {
|
|
22
22
|
* expectResult(TernaryResult.err('oops'));
|
|
@@ -31,9 +31,9 @@ export function expectToBe<R extends UnknownTernaryResult>(
|
|
|
31
31
|
): UnwrapOk<R>;
|
|
32
32
|
|
|
33
33
|
// Curried version
|
|
34
|
-
export function expectToBe
|
|
34
|
+
export function expectToBe(
|
|
35
35
|
message: string,
|
|
36
|
-
): <W, E>(result: TernaryResult<S, E, W>) => S;
|
|
36
|
+
): <S, W, E>(result: TernaryResult<S, E, W>) => S;
|
|
37
37
|
|
|
38
38
|
export function expectToBe<R extends UnknownTernaryResult>(
|
|
39
39
|
...args: readonly [result: R, message: string] | readonly [message: string]
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type TernaryResult,
|
|
3
|
-
type UnknownTernaryResult,
|
|
4
|
-
} from '../ternary-result.mjs';
|
|
1
|
+
import { type UnknownTernaryResult } from '../ternary-result.mjs';
|
|
5
2
|
import { isOk } from './ternary-result-is-ok.mjs';
|
|
6
3
|
import { isWarn } from './ternary-result-is-warn.mjs';
|
|
7
4
|
import { type NarrowToOk, type NarrowToWarn } from './types.mjs';
|
|
@@ -36,11 +33,11 @@ export function orElse<
|
|
|
36
33
|
>(result: R, alternative: R2): NarrowToOk<R> | NarrowToWarn<R> | R2;
|
|
37
34
|
|
|
38
35
|
// Curried version
|
|
39
|
-
export function orElse<
|
|
40
|
-
alternative:
|
|
41
|
-
): (
|
|
42
|
-
result:
|
|
43
|
-
) =>
|
|
36
|
+
export function orElse<R2 extends UnknownTernaryResult>(
|
|
37
|
+
alternative: R2,
|
|
38
|
+
): <R extends UnknownTernaryResult>(
|
|
39
|
+
result: R,
|
|
40
|
+
) => NarrowToOk<R> | NarrowToWarn<R> | R2;
|
|
44
41
|
|
|
45
42
|
export function orElse<
|
|
46
43
|
R extends UnknownTernaryResult,
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type TernaryResult,
|
|
3
|
-
type UnknownTernaryResult,
|
|
4
|
-
} from '../ternary-result.mjs';
|
|
1
|
+
import { type UnknownTernaryResult } from '../ternary-result.mjs';
|
|
5
2
|
import { unwrapErr } from './ternary-result-unwrap-err.mjs';
|
|
6
3
|
import { type UnwrapErr } from './types.mjs';
|
|
7
4
|
|
|
@@ -34,9 +31,9 @@ export function unwrapErrOr<R extends UnknownTernaryResult, D>(
|
|
|
34
31
|
): D | UnwrapErr<R>;
|
|
35
32
|
|
|
36
33
|
// Curried version
|
|
37
|
-
export function unwrapErrOr<
|
|
34
|
+
export function unwrapErrOr<D>(
|
|
38
35
|
defaultValue: D,
|
|
39
|
-
): <
|
|
36
|
+
): <R extends UnknownTernaryResult>(result: R) => D | UnwrapErr<R>;
|
|
40
37
|
|
|
41
38
|
export function unwrapErrOr<R extends UnknownTernaryResult, D>(
|
|
42
39
|
...args: readonly [result: R, defaultValue: D] | readonly [defaultValue: D]
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type TernaryResult,
|
|
3
|
-
type UnknownTernaryResult,
|
|
4
|
-
} from '../ternary-result.mjs';
|
|
1
|
+
import { type UnknownTernaryResult } from '../ternary-result.mjs';
|
|
5
2
|
import { unwrapOk } from './ternary-result-unwrap-ok.mjs';
|
|
6
3
|
import { type UnwrapOk } from './types.mjs';
|
|
7
4
|
|
|
@@ -31,9 +28,9 @@ export function unwrapOkOr<R extends UnknownTernaryResult, D>(
|
|
|
31
28
|
): D | UnwrapOk<R>;
|
|
32
29
|
|
|
33
30
|
// Curried version
|
|
34
|
-
export function unwrapOkOr<
|
|
31
|
+
export function unwrapOkOr<D>(
|
|
35
32
|
defaultValue: D,
|
|
36
|
-
): <
|
|
33
|
+
): <R extends UnknownTernaryResult>(result: R) => D | UnwrapOk<R>;
|
|
37
34
|
|
|
38
35
|
export function unwrapOkOr<R extends UnknownTernaryResult, D>(
|
|
39
36
|
...args: readonly [result: R, defaultValue: D] | readonly [defaultValue: D]
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type TernaryResult,
|
|
3
|
-
type UnknownTernaryResult,
|
|
4
|
-
} from '../ternary-result.mjs';
|
|
1
|
+
import { type UnknownTernaryResult } from '../ternary-result.mjs';
|
|
5
2
|
import { unwrapWarn } from './ternary-result-unwrap-warn.mjs';
|
|
6
3
|
import { type UnwrapWarn } from './types.mjs';
|
|
7
4
|
|
|
@@ -28,9 +25,9 @@ export function unwrapWarnOr<R extends UnknownTernaryResult, D>(
|
|
|
28
25
|
): D | UnwrapWarn<R>;
|
|
29
26
|
|
|
30
27
|
// Curried version
|
|
31
|
-
export function unwrapWarnOr<
|
|
28
|
+
export function unwrapWarnOr<D>(
|
|
32
29
|
defaultValue: D,
|
|
33
|
-
): <
|
|
30
|
+
): <R extends UnknownTernaryResult>(result: R) => D | UnwrapWarn<R>;
|
|
34
31
|
|
|
35
32
|
export function unwrapWarnOr<R extends UnknownTernaryResult, D>(
|
|
36
33
|
...args: readonly [result: R, defaultValue: D] | readonly [defaultValue: D]
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* - Excludes functions (they have `typeof` === `"function"`, not `"object"`)
|
|
14
14
|
* - Includes arrays, dates, regex, and other object instances
|
|
15
15
|
*
|
|
16
|
-
* **Note:** This function returns `true` for arrays. If you need
|
|
17
|
-
*
|
|
16
|
+
* **Note:** This function returns `true` for arrays. If you need
|
|
17
|
+
* record-oriented narrowing that excludes arrays, use `isRecord()` instead.
|
|
18
18
|
*
|
|
19
19
|
* @example
|
|
20
20
|
*
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* @param u - The value to check
|
|
30
30
|
* @returns `true` if `u` is an object and not `null`, `false` otherwise. When
|
|
31
31
|
* `true`, TypeScript narrows the type to `object`.
|
|
32
|
-
* @see {@link isRecord} - For
|
|
32
|
+
* @see {@link isRecord} - For record-oriented type narrowing (excludes arrays)
|
|
33
33
|
*/
|
|
34
34
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
|
35
35
|
export const isNonNullObject = (u: unknown): u is object =>
|
package/src/guard/is-record.mts
CHANGED
|
@@ -2,31 +2,46 @@ import { type MutableRecord, type UnknownRecord } from 'ts-type-forge';
|
|
|
2
2
|
import { isNonNullObject } from './is-non-null-object.mjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Type guard that checks if a value is a
|
|
6
|
-
* object
|
|
5
|
+
* Type guard that checks if a value is a record — a non-null, non-array
|
|
6
|
+
* object — and narrows it to `UnknownRecord` (= `ReadonlyRecord<string,
|
|
7
|
+
* unknown>`).
|
|
7
8
|
*
|
|
8
|
-
* This function is
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* types like Date, RegExp, etc., focusing on objects that can be safely treated
|
|
12
|
-
* as property collections.
|
|
9
|
+
* This function is designed to be combined with {@link hasKey} to probe
|
|
10
|
+
* values of type `unknown` (e.g. parsed JSON, caught errors, external data)
|
|
11
|
+
* in a type-safe way: `if (isRecord(u) && hasKey(u, 'some-key')) { ... }`.
|
|
13
12
|
*
|
|
14
13
|
* **Type Narrowing Behavior:**
|
|
15
14
|
*
|
|
16
|
-
* - Narrows `unknown` to `UnknownRecord` (
|
|
17
|
-
*
|
|
18
|
-
* - Excludes
|
|
19
|
-
*
|
|
20
|
-
* with
|
|
21
|
-
* - Returns `
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
15
|
+
* - Narrows `unknown` to `UnknownRecord` (= `ReadonlyRecord<string, unknown>`)
|
|
16
|
+
* - Excludes `null`, `undefined`, primitives, and functions
|
|
17
|
+
* - Excludes arrays: arrays have no string index signature, so they are not
|
|
18
|
+
* assignable to `UnknownRecord`; admitting them at runtime would be
|
|
19
|
+
* inconsistent with the narrowed type
|
|
20
|
+
* - Returns `true` for every other non-null object, including dictionary
|
|
21
|
+
* objects created with `Object.create(null)` and instances such as `Date`,
|
|
22
|
+
* `RegExp`, `Map`, `Set`, `Error`, and user-defined classes
|
|
23
|
+
*
|
|
24
|
+
* **Why non-plain objects are included:** since every property of
|
|
25
|
+
* `UnknownRecord` is typed `unknown`, reading properties from any object
|
|
26
|
+
* through this narrowing is type-safe, and `hasKey` (backed by
|
|
27
|
+
* `Object.hasOwn`) answers own-property questions correctly on any object
|
|
28
|
+
* (e.g. `Map` entries are not own properties, so `hasKey` correctly returns
|
|
29
|
+
* `false` for them). Restricting the guard to plain objects would require
|
|
30
|
+
* prototype checks that misjudge cross-realm objects and would prevent
|
|
31
|
+
* probing useful instances such as `Error` subclasses.
|
|
32
|
+
*
|
|
33
|
+
* **Implementation:** `isNonNullObject(u) && !Array.isArray(u)`.
|
|
25
34
|
*
|
|
26
35
|
* @example
|
|
27
36
|
*
|
|
28
37
|
* ```ts
|
|
29
|
-
* const entries: readonly unknown[] = [
|
|
38
|
+
* const entries: readonly unknown[] = [
|
|
39
|
+
* { id: 1 },
|
|
40
|
+
* [1, 2],
|
|
41
|
+
* 'str',
|
|
42
|
+
* 0,
|
|
43
|
+
* null,
|
|
44
|
+
* ] as const;
|
|
30
45
|
*
|
|
31
46
|
* const records = entries.filter(isRecord);
|
|
32
47
|
*
|
|
@@ -34,30 +49,33 @@ import { isNonNullObject } from './is-non-null-object.mjs';
|
|
|
34
49
|
* ```
|
|
35
50
|
*
|
|
36
51
|
* @param u - The value to check
|
|
37
|
-
* @returns `true` if `u` is a non-null object
|
|
38
|
-
*
|
|
39
|
-
* @see {@link isNonNullObject} - For checking any object
|
|
52
|
+
* @returns `true` if `u` is a non-null, non-array object, `false` otherwise.
|
|
53
|
+
* When `true`, TypeScript narrows the type to `UnknownRecord`.
|
|
54
|
+
* @see {@link isNonNullObject} - For checking any non-null object (includes arrays)
|
|
40
55
|
* @see {@link hasKey} - For checking if a record has specific keys
|
|
41
56
|
*/
|
|
42
|
-
export const isRecord = (u: unknown): u is UnknownRecord =>
|
|
57
|
+
export const isRecord = (u: unknown): u is UnknownRecord =>
|
|
58
|
+
isNonNullObject(u) && !Array.isArray(u);
|
|
43
59
|
|
|
44
60
|
/**
|
|
45
|
-
* Type guard that checks if a value is a mutable record
|
|
61
|
+
* Type guard that checks if a value is a mutable record.
|
|
46
62
|
*
|
|
47
|
-
* This function
|
|
48
|
-
*
|
|
49
|
-
*
|
|
63
|
+
* This function has the same runtime behavior as {@link isRecord} (non-null,
|
|
64
|
+
* non-array object check), but narrows the type to `MutableRecord<string,
|
|
65
|
+
* unknown>`, which is useful when you need to add or overwrite properties on
|
|
66
|
+
* the narrowed object.
|
|
50
67
|
*
|
|
51
68
|
* **Type Narrowing Behavior:**
|
|
52
69
|
*
|
|
53
70
|
* - Narrows `unknown` to `MutableRecord<string, unknown>`
|
|
54
|
-
* - Excludes `null`, `undefined`, primitives,
|
|
55
|
-
* - Returns `true` for
|
|
56
|
-
* -
|
|
71
|
+
* - Excludes `null`, `undefined`, primitives, functions, and arrays
|
|
72
|
+
* - Returns `true` for every other non-null object (see {@link isRecord} for
|
|
73
|
+
* details on non-plain objects such as `Date`, `Map`, and `Set`)
|
|
57
74
|
*
|
|
58
75
|
* **Implementation:** This is a type alias for {@link isRecord}.
|
|
59
76
|
*
|
|
60
77
|
* @example
|
|
78
|
+
*
|
|
61
79
|
* ```ts
|
|
62
80
|
* const obj: unknown = { foo: 1 } as const;
|
|
63
81
|
*
|
|
@@ -67,8 +85,9 @@ export const isRecord = (u: unknown): u is UnknownRecord => isNonNullObject(u);
|
|
|
67
85
|
* ```
|
|
68
86
|
*
|
|
69
87
|
* @param u - The value to check
|
|
70
|
-
* @returns `true` if `u` is a
|
|
71
|
-
* When `true`, TypeScript narrows the type to `MutableRecord<string,
|
|
88
|
+
* @returns `true` if `u` is a non-null, non-array object, `false` otherwise.
|
|
89
|
+
* When `true`, TypeScript narrows the type to `MutableRecord<string,
|
|
90
|
+
* unknown>`.
|
|
72
91
|
* @see {@link isRecord} - For the underlying implementation and more details
|
|
73
92
|
*/
|
|
74
93
|
export const isMutableRecord: (
|