ts-fortress 2.0.4 → 2.2.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 +80 -1
- package/dist/index.mjs +1 -1
- package/dist/record/index.mjs +1 -1
- package/dist/record/key-value-record.d.mts +1 -1
- package/dist/record/key-value-record.d.mts.map +1 -1
- package/dist/record/record.d.mts +31 -5
- package/dist/record/record.d.mts.map +1 -1
- package/dist/record/record.mjs +42 -4
- package/dist/record/record.mjs.map +1 -1
- package/package.json +1 -1
- package/src/array/array-of-length.test.mts +16 -1
- package/src/array/array.test.mts +16 -1
- package/src/array/non-empty-array.test.mts +16 -1
- package/src/array/tuple.test.mts +18 -1
- package/src/compose/intersection.test.mts +43 -1
- package/src/compose/merge-records.test.mts +16 -1
- package/src/compose/union.test.mts +16 -1
- package/src/enum/enum.test.mts +16 -1
- package/src/enum/uint-range.test.mts +14 -1
- package/src/primitives/null.test.mts +170 -0
- package/src/primitives/number.test.mts +284 -0
- package/src/primitives/string.test.mts +341 -0
- package/src/primitives/undefined.test.mts +179 -0
- package/src/record/key-value-record.mts +4 -4
- package/src/record/key-value-record.test.mts +28 -2
- package/src/record/keyof.test.mts +12 -0
- package/src/record/partial.test.mts +18 -0
- package/src/record/record-allow-excess-properties.test.mts +180 -0
- package/src/record/record-strict-composition-simple.test.mts +134 -0
- package/src/record/record-strict-composition.test.mts +403 -0
- package/src/record/record.mts +92 -31
- package/src/record/record.test.mts +71 -1
- package/src/record/strict-record.test.mts +192 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { expectType, Result } from 'ts-data-forge';
|
|
2
|
+
import { number, string } from '../primitives/index.mjs';
|
|
3
|
+
import { type TypeOf } from '../type.mjs';
|
|
4
|
+
import { validationErrorsToMessages } from '../validation-error.mjs';
|
|
5
|
+
import { record, strictRecord } from './record.mjs';
|
|
6
|
+
|
|
7
|
+
describe('strictRecord', () => {
|
|
8
|
+
const userType = strictRecord({
|
|
9
|
+
name: string(''),
|
|
10
|
+
age: number(0),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
type User = TypeOf<typeof userType>;
|
|
14
|
+
expectType<User, Readonly<{ name: string; age: number }>>('=');
|
|
15
|
+
|
|
16
|
+
describe('is', () => {
|
|
17
|
+
test('valid data without excess properties', () => {
|
|
18
|
+
const validUser = { name: 'John', age: 30 };
|
|
19
|
+
|
|
20
|
+
expect(userType.is(validUser)).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('rejects data with excess properties', () => {
|
|
24
|
+
const userWithExtra = { name: 'John', age: 30, extra: 'not allowed' };
|
|
25
|
+
|
|
26
|
+
expect(userType.is(userWithExtra)).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('rejects invalid property types', () => {
|
|
30
|
+
const invalidUser = { name: 'John', age: 'thirty' };
|
|
31
|
+
|
|
32
|
+
expect(userType.is(invalidUser)).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('validate', () => {
|
|
37
|
+
test('truthy case - valid data', () => {
|
|
38
|
+
const validUser = { name: 'John', age: 30 };
|
|
39
|
+
|
|
40
|
+
const result = userType.validate(validUser);
|
|
41
|
+
expect(Result.isOk(result)).toBe(true);
|
|
42
|
+
|
|
43
|
+
if (Result.isOk(result)) {
|
|
44
|
+
expectType<typeof result.value, User>('=');
|
|
45
|
+
expect(result.value).toStrictEqual({
|
|
46
|
+
name: 'John',
|
|
47
|
+
age: 30,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('falsy case - excess properties', () => {
|
|
53
|
+
const userWithExtra = { name: 'John', age: 30, extra: 'not allowed' };
|
|
54
|
+
|
|
55
|
+
const result = userType.validate(userWithExtra);
|
|
56
|
+
expect(Result.isErr(result)).toBe(true);
|
|
57
|
+
|
|
58
|
+
if (Result.isErr(result)) {
|
|
59
|
+
expect(result.value).toStrictEqual([
|
|
60
|
+
{
|
|
61
|
+
path: ['extra'],
|
|
62
|
+
actualValue: 'not allowed',
|
|
63
|
+
expectedType: '{ name: string, age: number }',
|
|
64
|
+
typeName: '{ name: string, age: number }',
|
|
65
|
+
message: 'Excess property "extra" is not allowed',
|
|
66
|
+
},
|
|
67
|
+
]);
|
|
68
|
+
expect(validationErrorsToMessages(result.value)).toStrictEqual([
|
|
69
|
+
'Excess property "extra" is not allowed at extra',
|
|
70
|
+
]);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('falsy case - invalid property type', () => {
|
|
75
|
+
const invalidUser = { name: 'John', age: 'thirty' };
|
|
76
|
+
|
|
77
|
+
const result = userType.validate(invalidUser);
|
|
78
|
+
expect(Result.isErr(result)).toBe(true);
|
|
79
|
+
|
|
80
|
+
if (Result.isErr(result)) {
|
|
81
|
+
expect(result.value).toStrictEqual([
|
|
82
|
+
{
|
|
83
|
+
path: ['age'],
|
|
84
|
+
actualValue: 'thirty',
|
|
85
|
+
expectedType: 'number',
|
|
86
|
+
typeName: 'number',
|
|
87
|
+
message: undefined,
|
|
88
|
+
},
|
|
89
|
+
]);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('falsy case - missing required property', () => {
|
|
94
|
+
const incompleteUser = { name: 'John' };
|
|
95
|
+
|
|
96
|
+
const result = userType.validate(incompleteUser);
|
|
97
|
+
expect(Result.isErr(result)).toBe(true);
|
|
98
|
+
|
|
99
|
+
if (Result.isErr(result)) {
|
|
100
|
+
expect(result.value).toStrictEqual([
|
|
101
|
+
{
|
|
102
|
+
path: ['age'],
|
|
103
|
+
actualValue: incompleteUser,
|
|
104
|
+
expectedType: '{ name: string, age: number }',
|
|
105
|
+
typeName: '{ name: string, age: number }',
|
|
106
|
+
message: 'Missing required key "age"',
|
|
107
|
+
},
|
|
108
|
+
]);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('comparison with regular record', () => {
|
|
114
|
+
const regularRecord = record({
|
|
115
|
+
name: string(''),
|
|
116
|
+
age: number(0),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('regular record allows excess properties', () => {
|
|
120
|
+
const dataWithExtra = { name: 'John', age: 30, extra: 'allowed' };
|
|
121
|
+
|
|
122
|
+
expect(regularRecord.is(dataWithExtra)).toBe(true);
|
|
123
|
+
expect(
|
|
124
|
+
strictRecord({ name: string(''), age: number(0) }).is(dataWithExtra),
|
|
125
|
+
).toBe(false);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('both reject invalid property types', () => {
|
|
129
|
+
const invalidData = { name: 'John', age: 'thirty' };
|
|
130
|
+
|
|
131
|
+
expect(regularRecord.is(invalidData)).toBe(false);
|
|
132
|
+
expect(
|
|
133
|
+
strictRecord({ name: string(''), age: number(0) }).is(invalidData),
|
|
134
|
+
).toBe(false);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe('with custom typeName', () => {
|
|
139
|
+
test('uses custom typeName in errors', () => {
|
|
140
|
+
const customUserType = strictRecord(
|
|
141
|
+
{
|
|
142
|
+
name: string(''),
|
|
143
|
+
age: number(0),
|
|
144
|
+
},
|
|
145
|
+
{ typeName: 'User' },
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const result = customUserType.validate({
|
|
149
|
+
name: 'John',
|
|
150
|
+
age: 30,
|
|
151
|
+
extra: 'not allowed',
|
|
152
|
+
});
|
|
153
|
+
expect(Result.isErr(result)).toBe(true);
|
|
154
|
+
|
|
155
|
+
if (Result.isErr(result)) {
|
|
156
|
+
expect(result.value[0]?.expectedType).toBe('User');
|
|
157
|
+
expect(result.value[0]?.typeName).toBe('User');
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('default values', () => {
|
|
163
|
+
test('has correct default value', () => {
|
|
164
|
+
expect(userType.defaultValue).toStrictEqual({
|
|
165
|
+
name: '',
|
|
166
|
+
age: 0,
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('fill', () => {
|
|
172
|
+
test('fills missing properties with defaults', () => {
|
|
173
|
+
const partialData = { name: 'John' };
|
|
174
|
+
|
|
175
|
+
const result = userType.fill(partialData);
|
|
176
|
+
expect(result).toStrictEqual({
|
|
177
|
+
name: 'John',
|
|
178
|
+
age: 0,
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test('preserves all valid properties', () => {
|
|
183
|
+
const validData = { name: 'John', age: 30 };
|
|
184
|
+
|
|
185
|
+
const result = userType.fill(validData);
|
|
186
|
+
expect(result).toStrictEqual({
|
|
187
|
+
name: 'John',
|
|
188
|
+
age: 30,
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
});
|