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
|
@@ -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('
|
|
7
|
-
test('
|
|
8
|
-
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
|
-
|
|
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
|
-
|
|
27
|
+
assert.isTrue(isRecord(unk));
|
|
15
28
|
});
|
|
29
|
+
});
|
|
16
30
|
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
38
|
+
expectType<readonly number[], UnknownRecord>('!<=');
|
|
21
39
|
|
|
22
|
-
|
|
40
|
+
expectType<readonly string[], Record<number, unknown>>('<=');
|
|
41
|
+
|
|
42
|
+
expectType<readonly string[], UnknownRecord>('!<=');
|
|
23
43
|
|
|
24
|
-
expectType<
|
|
44
|
+
expectType<readonly [], UnknownRecord>('!<=');
|
|
25
45
|
});
|
|
26
46
|
|
|
27
|
-
test('[] is a record', () => {
|
|
28
|
-
const
|
|
47
|
+
test('[1, 2, 3] is not a record', () => {
|
|
48
|
+
const unk: unknown = [1, 2, 3] as const;
|
|
29
49
|
|
|
30
|
-
|
|
50
|
+
assert.isFalse(isRecord(unk));
|
|
51
|
+
});
|
|
31
52
|
|
|
32
|
-
|
|
53
|
+
test('["1", "2", "3"] is not a record', () => {
|
|
54
|
+
const unk: unknown = ['1', '2', '3'] as const;
|
|
33
55
|
|
|
34
|
-
|
|
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
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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('
|
|
50
|
-
|
|
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
|
-
|
|
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
|
-
|
|
136
|
+
assert.isFalse(isRecord(unk));
|
|
57
137
|
});
|
|
58
138
|
});
|
|
59
139
|
|
|
60
|
-
describe('primitives are not
|
|
140
|
+
describe('primitives are not records', () => {
|
|
61
141
|
test('null is not a record', () => {
|
|
62
|
-
const
|
|
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
|
|
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
|
|
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
|
|
93
|
-
|
|
94
|
-
const unk: unknown = prm;
|
|
160
|
+
const unk: unknown = 'str';
|
|
95
161
|
|
|
96
162
|
assert.isFalse(isRecord(unk));
|
|
163
|
+
});
|
|
97
164
|
|
|
98
|
-
|
|
165
|
+
test('true is not a record', () => {
|
|
166
|
+
const unk: unknown = true;
|
|
167
|
+
|
|
168
|
+
assert.isFalse(isRecord(unk));
|
|
99
169
|
});
|
|
100
|
-
});
|
|
101
170
|
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
108
|
-
|
|
174
|
+
assert.isFalse(isRecord(unk));
|
|
175
|
+
});
|
|
109
176
|
|
|
110
|
-
|
|
111
|
-
|
|
177
|
+
test('bigint is not a record', () => {
|
|
178
|
+
const unk: unknown = 1n;
|
|
112
179
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
119
|
-
|
|
187
|
+
if (isRecord(unk) && hasKey(unk, 'some-key')) {
|
|
188
|
+
expectType<(typeof unk)['some-key'], unknown>('=');
|
|
120
189
|
|
|
121
|
-
|
|
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) &&
|