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.
Files changed (60) hide show
  1. package/dist/array/impl/array-utils-creation.d.mts +2 -2
  2. package/dist/array/impl/array-utils-creation.d.mts.map +1 -1
  3. package/dist/functional/optional/impl/optional-expect-to-be.d.mts +2 -2
  4. package/dist/functional/optional/impl/optional-expect-to-be.d.mts.map +1 -1
  5. package/dist/functional/optional/impl/optional-or-else.d.mts +1 -1
  6. package/dist/functional/optional/impl/optional-or-else.d.mts.map +1 -1
  7. package/dist/functional/optional/impl/optional-unwrap-or.d.mts +1 -1
  8. package/dist/functional/optional/impl/optional-unwrap-or.d.mts.map +1 -1
  9. package/dist/functional/result/impl/result-expect-to-be.d.mts +3 -3
  10. package/dist/functional/result/impl/result-expect-to-be.d.mts.map +1 -1
  11. package/dist/functional/result/impl/result-expect-to-be.mjs.map +1 -1
  12. package/dist/functional/result/impl/result-or-else.d.mts +2 -2
  13. package/dist/functional/result/impl/result-or-else.d.mts.map +1 -1
  14. package/dist/functional/result/impl/result-unwrap-err-or.d.mts +2 -2
  15. package/dist/functional/result/impl/result-unwrap-err-or.d.mts.map +1 -1
  16. package/dist/functional/result/impl/result-unwrap-ok-or.d.mts +2 -2
  17. package/dist/functional/result/impl/result-unwrap-ok-or.d.mts.map +1 -1
  18. package/dist/functional/ternary-result/impl/ternary-result-expect-to-be.d.mts +2 -2
  19. package/dist/functional/ternary-result/impl/ternary-result-expect-to-be.d.mts.map +1 -1
  20. package/dist/functional/ternary-result/impl/ternary-result-or-else.d.mts +2 -2
  21. package/dist/functional/ternary-result/impl/ternary-result-or-else.d.mts.map +1 -1
  22. package/dist/functional/ternary-result/impl/ternary-result-or-else.mjs.map +1 -1
  23. package/dist/functional/ternary-result/impl/ternary-result-unwrap-err-or.d.mts +2 -2
  24. package/dist/functional/ternary-result/impl/ternary-result-unwrap-err-or.d.mts.map +1 -1
  25. package/dist/functional/ternary-result/impl/ternary-result-unwrap-err-or.mjs.map +1 -1
  26. package/dist/functional/ternary-result/impl/ternary-result-unwrap-ok-or.d.mts +2 -2
  27. package/dist/functional/ternary-result/impl/ternary-result-unwrap-ok-or.d.mts.map +1 -1
  28. package/dist/functional/ternary-result/impl/ternary-result-unwrap-ok-or.mjs.map +1 -1
  29. package/dist/functional/ternary-result/impl/ternary-result-unwrap-warn-or.d.mts +2 -2
  30. package/dist/functional/ternary-result/impl/ternary-result-unwrap-warn-or.d.mts.map +1 -1
  31. package/dist/functional/ternary-result/impl/ternary-result-unwrap-warn-or.mjs.map +1 -1
  32. package/dist/guard/is-non-null-object.d.mts +3 -3
  33. package/dist/guard/is-non-null-object.mjs +3 -3
  34. package/dist/guard/is-record.d.mts +47 -29
  35. package/dist/guard/is-record.d.mts.map +1 -1
  36. package/dist/guard/is-record.mjs +48 -30
  37. package/dist/guard/is-record.mjs.map +1 -1
  38. package/dist/others/fast-deep-equal.d.mts.map +1 -1
  39. package/dist/others/fast-deep-equal.mjs +7 -5
  40. package/dist/others/fast-deep-equal.mjs.map +1 -1
  41. package/package.json +4 -4
  42. package/src/array/impl/array-utils-creation.mts +1 -1
  43. package/src/array/impl/array-utils-transformation.mts +1 -1
  44. package/src/functional/optional/impl/optional-expect-to-be.mts +2 -2
  45. package/src/functional/optional/impl/optional-or-else.mts +3 -3
  46. package/src/functional/optional/impl/optional-unwrap-or.mts +2 -2
  47. package/src/functional/optional.test.mts +3 -3
  48. package/src/functional/result/impl/result-expect-to-be.mts +5 -3
  49. package/src/functional/result/impl/result-or-else.mts +4 -4
  50. package/src/functional/result/impl/result-unwrap-err-or.mts +2 -2
  51. package/src/functional/result/impl/result-unwrap-ok-or.mts +3 -3
  52. package/src/functional/ternary-result/impl/ternary-result-expect-to-be.mts +3 -3
  53. package/src/functional/ternary-result/impl/ternary-result-or-else.mts +6 -9
  54. package/src/functional/ternary-result/impl/ternary-result-unwrap-err-or.mts +3 -6
  55. package/src/functional/ternary-result/impl/ternary-result-unwrap-ok-or.mts +3 -6
  56. package/src/functional/ternary-result/impl/ternary-result-unwrap-warn-or.mts +3 -6
  57. package/src/guard/is-non-null-object.mts +3 -3
  58. package/src/guard/is-record.mts +49 -30
  59. package/src/guard/is-record.test.mts +133 -61
  60. package/src/others/fast-deep-equal.mts +10 -8
@@ -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 plain object (record) - a non-null
5
- * object that is not an array.
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 useful for identifying "plain" JavaScript objects (also
8
- * called records or dictionaries) - objects that are typically used as
9
- * key-value collections. It excludes arrays, functions, and special object
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` (equivalent to `Record<PropertyKey,
16
- * unknown>`)
17
- * - Excludes `null`, `undefined`, primitives, arrays, and functions
18
- * - Returns `true` for plain objects `{}`, object literals, and objects created
19
- * with `Object.create()`
20
- * - Returns `false` for arrays, even though they are technically objects
21
- *
22
- * **Implementation:** Uses `isNonNullObject()` to check for objects, then
23
- * `Array.isArray()` to exclude arrays.
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[] = [{ id: 1 }, 'str', 0, null] as const;
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 and not an array, `false`
37
- * otherwise. When `true`, TypeScript narrows the type to `UnknownRecord`.
38
- * @see {@link isNonNullObject} - For checking any object type (includes arrays)
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 (an object with mutable string keys).
58
+ * Type guard that checks if a value is a mutable record.
44
59
  *
45
- * This function is an alias for {@link isRecord}, but narrows the type to
46
- * `MutableRecord<string, unknown>`, which is useful when you need to ensure
47
- * that the object can be safely mutated (i.e., its properties can be added or changed).
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, arrays, and functions
53
- * - Returns `true` for plain objects with mutable string keys
54
- * - Returns `false` for arrays and other non-record types
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 mutable record (plain object with string keys), `false` otherwise.
69
- * When `true`, TypeScript narrows the type to `MutableRecord<string, unknown>`.
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCG;AACI,MAAM,QAAQ,GAAG,CAAC,CAAU,KAAyB,eAAe,CAAC,CAAC;AAE7E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BG;AACI,MAAM,eAAe,GAEe;;;;"}
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,OAqF9C,CAAC"}
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":"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"}
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": "7.1.0",
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.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": "9.3.1",
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.16",
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 = <const V, N extends SizeType.ArgArr>(
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<S, PositiveUint32>(asPositiveUint32(array.length + 1), init),
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<string>('missing optional');
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<S>(message: string): (optional: Optional<S>) => S;
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<S, S2>(
51
- alternative: Optional<S2>,
52
- ): (optional: Optional<S>) => Optional<S> | Optional<S2>;
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<S, D>(
43
+ export function unwrapOr<D>(
44
44
  defaultValue: D,
45
- ): (optional: Optional<S>) => D | S;
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<number>('Expected a number');
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<number>('Expected a number');
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<string>('ID is required');
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 Result, type UnknownResult } from '../result.mjs';
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<string>('missing result');
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<S>(message: string): <E>(result: Result<S, E>) => S;
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 Result, type UnknownResult } from '../result.mjs';
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<S, E, S2, E2>(
47
- alternative: Result<S2, E2>,
48
- ): (result: Result<S, E>) => Result<S, E> | Result<S2, E2>;
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 Result, type UnknownResult } from '../result.mjs';
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
- ): <S, E>(result: Result<S, E>) => D | E;
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 Result, type UnknownResult } from '../result.mjs';
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<S, D>(
39
+ export function unwrapOkOr<D>(
40
40
  defaultValue: D,
41
- ): <E>(result: Result<S, E>) => D | S;
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<string>('needs value');
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<S>(
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<S, W, E, S2, W2, E2>(
40
- alternative: TernaryResult<S2, E2, W2>,
41
- ): (
42
- result: TernaryResult<S, E, W>,
43
- ) => TernaryResult<S, E, W> | TernaryResult<S2, E2, W2>;
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<E, D>(
34
+ export function unwrapErrOr<D>(
38
35
  defaultValue: D,
39
- ): <S, W>(result: TernaryResult<S, E, W>) => D | E;
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<S, D>(
31
+ export function unwrapOkOr<D>(
35
32
  defaultValue: D,
36
- ): <W, E>(result: TernaryResult<S, E, W>) => D | S;
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<W, D>(
28
+ export function unwrapWarnOr<D>(
32
29
  defaultValue: D,
33
- ): <S, E>(result: TernaryResult<S, E, W>) => D | W;
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 to check for
17
- * plain objects specifically (excluding arrays), use `isRecord()` instead.
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 checking plain objects specifically (excludes arrays)
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 =>
@@ -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 plain object (record) - a non-null
6
- * object that is not an array.
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 useful for identifying "plain" JavaScript objects (also
9
- * called records or dictionaries) - objects that are typically used as
10
- * key-value collections. It excludes arrays, functions, and special object
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` (equivalent to `Record<PropertyKey,
17
- * unknown>`)
18
- * - Excludes `null`, `undefined`, primitives, arrays, and functions
19
- * - Returns `true` for plain objects `{}`, object literals, and objects created
20
- * with `Object.create()`
21
- * - Returns `false` for arrays, even though they are technically objects
22
- *
23
- * **Implementation:** Uses `isNonNullObject()` to check for objects, then
24
- * `Array.isArray()` to exclude arrays.
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[] = [{ id: 1 }, 'str', 0, null] as const;
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 and not an array, `false`
38
- * otherwise. When `true`, TypeScript narrows the type to `UnknownRecord`.
39
- * @see {@link isNonNullObject} - For checking any object type (includes arrays)
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 => isNonNullObject(u);
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 (an object with mutable string keys).
61
+ * Type guard that checks if a value is a mutable record.
46
62
  *
47
- * This function is an alias for {@link isRecord}, but narrows the type to
48
- * `MutableRecord<string, unknown>`, which is useful when you need to ensure
49
- * that the object can be safely mutated (i.e., its properties can be added or changed).
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, arrays, and functions
55
- * - Returns `true` for plain objects with mutable string keys
56
- * - Returns `false` for arrays and other non-record types
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 mutable record (plain object with string keys), `false` otherwise.
71
- * When `true`, TypeScript narrows the type to `MutableRecord<string, unknown>`.
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: (