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,123 +1,195 @@
1
1
  import { type UnknownRecord } from 'ts-type-forge';
2
2
  import { expectType } from '../expect-type.mjs';
3
+ import { hasKey } from './has-key.mjs';
3
4
  import { isRecord } from './is-record.mjs';
4
5
 
5
6
  describe(isRecord, () => {
6
- describe('arrays are Records', () => {
7
- test('number[] is assignable to Record<number, unknown>', () => {
8
- const arr: Record<number, unknown> = [1, 2, 3] as const;
7
+ describe('plain objects are records', () => {
8
+ test('{ x: 1 } is a record', () => {
9
+ const unk: unknown = { x: 1 } as const;
10
+
11
+ if (isRecord(unk)) {
12
+ expectType<typeof unk, UnknownRecord>('=');
13
+ } else {
14
+ assert.fail();
15
+ }
16
+ });
9
17
 
10
- const unk: unknown = arr;
18
+ test('{} is a record', () => {
19
+ const unk: unknown = {} as const;
11
20
 
12
21
  assert.isTrue(isRecord(unk));
22
+ });
23
+
24
+ test('Object.create(null) is a record', () => {
25
+ const unk: unknown = Object.create(null);
13
26
 
14
- expectType<typeof unk, UnknownRecord>('<=');
27
+ assert.isTrue(isRecord(unk));
15
28
  });
29
+ });
16
30
 
17
- test('string[] is assignable to Record<number, unknown>', () => {
18
- const arr: Record<number, unknown> = ['1', '2', '3'] as const;
31
+ describe('arrays are not records', () => {
32
+ test('type-level: arrays are not assignable to UnknownRecord', () => {
33
+ // Arrays have a number index signature but no string index signature,
34
+ // so they are assignable to `Record<number, unknown>` but not to
35
+ // `UnknownRecord` (= `ReadonlyRecord<string, unknown>`).
36
+ expectType<readonly number[], Record<number, unknown>>('<=');
19
37
 
20
- const unk: unknown = arr;
38
+ expectType<readonly number[], UnknownRecord>('!<=');
21
39
 
22
- assert.isTrue(isRecord(unk));
40
+ expectType<readonly string[], Record<number, unknown>>('<=');
41
+
42
+ expectType<readonly string[], UnknownRecord>('!<=');
23
43
 
24
- expectType<typeof unk, UnknownRecord>('<=');
44
+ expectType<readonly [], UnknownRecord>('!<=');
25
45
  });
26
46
 
27
- test('[] is a record', () => {
28
- const arr: unknown = [] as const;
47
+ test('[1, 2, 3] is not a record', () => {
48
+ const unk: unknown = [1, 2, 3] as const;
29
49
 
30
- const unk: unknown = arr;
50
+ assert.isFalse(isRecord(unk));
51
+ });
31
52
 
32
- assert.isTrue(isRecord(unk));
53
+ test('["1", "2", "3"] is not a record', () => {
54
+ const unk: unknown = ['1', '2', '3'] as const;
33
55
 
34
- expectType<typeof unk, UnknownRecord>('<=');
56
+ assert.isFalse(isRecord(unk));
57
+ });
58
+
59
+ test('[] is not a record', () => {
60
+ const unk: unknown = [] as const;
61
+
62
+ assert.isFalse(isRecord(unk));
35
63
  });
36
64
  });
37
65
 
38
- describe('non-null objects are Records', () => {
39
- test('{ x: 1 } is a record', () => {
40
- const obj = { x: 1 } as const;
66
+ describe('other non-null objects are records', () => {
67
+ // These are not statically assignable to `UnknownRecord` either, but the
68
+ // guard intentionally admits them: every property read through
69
+ // `UnknownRecord` is typed `unknown`, so the narrowing is type-safe, and
70
+ // it keeps `isRecord(u) && hasKey(u, k)` usable for probing instances
71
+ // such as caught `Error` objects.
72
+ test('Date is a record', () => {
73
+ expectType<Date, UnknownRecord>('!<=');
74
+
75
+ const unk: unknown = new Date('2020-01-01');
76
+
77
+ assert.isTrue(isRecord(unk));
78
+ });
41
79
 
42
- const unk: unknown = obj;
80
+ test('RegExp is a record', () => {
81
+ const unk: unknown = /test/u;
43
82
 
44
83
  assert.isTrue(isRecord(unk));
84
+ });
85
+
86
+ test('Map is a record (but its entries are not own properties)', () => {
87
+ expectType<ReadonlyMap<string, number>, UnknownRecord>('!<=');
88
+
89
+ const unk: unknown = new Map([['a', 1]]);
45
90
 
46
- expectType<typeof unk, UnknownRecord>('<=');
91
+ if (isRecord(unk)) {
92
+ // Map entries are not own properties, so hasKey correctly rejects them.
93
+ assert.isFalse(hasKey(unk, 'a'));
94
+ } else {
95
+ assert.fail();
96
+ }
47
97
  });
48
98
 
49
- test('{} is a record', () => {
50
- const obj = {} as const;
99
+ test('Set is a record', () => {
100
+ expectType<ReadonlySet<number>, UnknownRecord>('!<=');
101
+
102
+ const unk: unknown = new Set([1, 2, 3]);
103
+
104
+ assert.isTrue(isRecord(unk));
105
+ });
106
+
107
+ test('Error is a record and its own properties can be probed', () => {
108
+ const unk: unknown = Object.assign(new Error('failure'), {
109
+ code: 'ENOENT',
110
+ });
111
+
112
+ if (isRecord(unk) && hasKey(unk, 'code')) {
113
+ expectType<(typeof unk)['code'], unknown>('=');
51
114
 
52
- const unk: unknown = obj;
115
+ assert.strictEqual(unk.code, 'ENOENT');
116
+ } else {
117
+ assert.fail();
118
+ }
119
+ });
120
+
121
+ test('class instance is a record', () => {
122
+ class Foo {
123
+ readonly x = 1;
124
+ }
125
+
126
+ const unk: unknown = new Foo();
53
127
 
54
128
  assert.isTrue(isRecord(unk));
129
+ });
130
+ });
131
+
132
+ describe('functions are not records', () => {
133
+ test('arrow function is not a record', () => {
134
+ const unk: unknown = () => 0;
55
135
 
56
- expectType<typeof unk, UnknownRecord>('<=');
136
+ assert.isFalse(isRecord(unk));
57
137
  });
58
138
  });
59
139
 
60
- describe('primitives are not Records', () => {
140
+ describe('primitives are not records', () => {
61
141
  test('null is not a record', () => {
62
- const obj = null;
63
-
64
- const unk: unknown = obj;
142
+ const unk: unknown = null;
65
143
 
66
144
  assert.isFalse(isRecord(unk));
67
-
68
- expectType<typeof unk, UnknownRecord>('!<=');
69
145
  });
70
146
 
71
147
  test('undefined is not a record', () => {
72
- const prm = undefined;
73
-
74
- const unk: unknown = prm;
148
+ const unk: unknown = undefined;
75
149
 
76
150
  assert.isFalse(isRecord(unk));
77
-
78
- expectType<typeof unk, UnknownRecord>('!<=');
79
151
  });
80
152
 
81
153
  test('3 is not a record', () => {
82
- const prm = 3;
83
-
84
- const unk: unknown = prm;
154
+ const unk: unknown = 3;
85
155
 
86
156
  assert.isFalse(isRecord(unk));
87
-
88
- expectType<typeof unk, UnknownRecord>('!<=');
89
157
  });
90
158
 
91
159
  test('"str" is not a record', () => {
92
- const prm = 'str';
93
-
94
- const unk: unknown = prm;
160
+ const unk: unknown = 'str';
95
161
 
96
162
  assert.isFalse(isRecord(unk));
163
+ });
97
164
 
98
- expectType<typeof unk, UnknownRecord>('!<=');
165
+ test('true is not a record', () => {
166
+ const unk: unknown = true;
167
+
168
+ assert.isFalse(isRecord(unk));
99
169
  });
100
- });
101
170
 
102
- // test('Map is not a record', () => {
103
- // const obj = new MutableMap();
104
- // const unk: unknown = obj;
105
- // const res = isRecord(unk);
171
+ test('symbol is not a record', () => {
172
+ const unk: unknown = Symbol('sym');
106
173
 
107
- // assertNotType<typeof obj, UnknownRecord>("<=");
108
- // expectType<typeof res, boolean>("=");
174
+ assert.isFalse(isRecord(unk));
175
+ });
109
176
 
110
- // expect(res).toBe(false);
111
- // });
177
+ test('bigint is not a record', () => {
178
+ const unk: unknown = 1n;
112
179
 
113
- // test('Set is not a record', () => {
114
- // const obj = new MutableSet();
115
- // const unk: unknown = obj;
116
- // const res = isRecord(unk);
180
+ assert.isFalse(isRecord(unk));
181
+ });
182
+ });
183
+
184
+ test('combined with hasKey', () => {
185
+ const unk: unknown = { 'some-key': 42 } as const;
117
186
 
118
- // assertNotType<typeof obj, UnknownRecord>("<=");
119
- // expectType<typeof res, boolean>("=");
187
+ if (isRecord(unk) && hasKey(unk, 'some-key')) {
188
+ expectType<(typeof unk)['some-key'], unknown>('=');
120
189
 
121
- // expect(res).toBe(false);
122
- // });
190
+ assert.strictEqual(unk['some-key'], 42);
191
+ } else {
192
+ assert.fail();
193
+ }
194
+ });
123
195
  });
@@ -16,6 +16,16 @@ export const fastDeepEqual = <T,>(a: T, b: T): boolean => {
16
16
  return false;
17
17
  }
18
18
 
19
+ // Arrays are handled before `isRecord`, which excludes them.
20
+ if (Array.isArray(a)) {
21
+ return (
22
+ Array.isArray(b) &&
23
+ a.constructor === b.constructor &&
24
+ a.length === b.length &&
25
+ a.every((ai, index) => fastDeepEqual(ai, b[index]))
26
+ );
27
+ }
28
+
19
29
  if (isRecord(a)) {
20
30
  if (!isRecord(b)) {
21
31
  return false;
@@ -25,14 +35,6 @@ export const fastDeepEqual = <T,>(a: T, b: T): boolean => {
25
35
  return false;
26
36
  }
27
37
 
28
- if (Array.isArray(a)) {
29
- return (
30
- Array.isArray(b) &&
31
- a.length === b.length &&
32
- a.every((ai, index) => fastDeepEqual(ai, b[index]))
33
- );
34
- }
35
-
36
38
  if (isMap(a)) {
37
39
  return (
38
40
  isMap(b) &&