rsult 1.4.0 → 2.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.
@@ -0,0 +1,409 @@
1
+ /**
2
+ * Type-Level Tests for Result and Option
3
+ *
4
+ * These tests verify that the type system behaves correctly at compile time.
5
+ * They use @ts-expect-error to ensure that invalid operations fail to compile.
6
+ *
7
+ * Run with: npx tsc --noEmit
8
+ */
9
+
10
+ import {
11
+ Result, ResultOk, ResultErr,
12
+ Ok, Err,
13
+ UnwrapOk, UnwrapErr, FlattenResult,
14
+ collect, matchResult,
15
+ } from './result';
16
+
17
+ import {
18
+ Option, OptionSome, OptionNone,
19
+ Some, None,
20
+ UnwrapOption, FlattenOption,
21
+ matchOption,
22
+ } from './option';
23
+
24
+ // ============================================================================
25
+ // Type Assertions Helper
26
+ // ============================================================================
27
+
28
+ type Expect<T extends true> = T;
29
+ type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2)
30
+ ? true
31
+ : false;
32
+ type NotEqual<X, Y> = Equal<X, Y> extends true ? false : true;
33
+
34
+ // ============================================================================
35
+ // Result Type-Level Tests
36
+ // ============================================================================
37
+
38
+ // --- Factory Function Types ---
39
+ {
40
+ const ok = Ok(42);
41
+ const err = Err("error");
42
+
43
+ // Ok should produce ResultOk<T, never>
44
+ type _TestOkType = Expect<Equal<typeof ok, ResultOk<number, never>>>;
45
+
46
+ // Err should produce ResultErr<never, E>
47
+ type _TestErrType = Expect<Equal<typeof err, ResultErr<never, string>>>;
48
+ }
49
+
50
+ // --- Type Guards ---
51
+ {
52
+ const result: Result<number, string> = Ok(42);
53
+
54
+ if (result.is_ok()) {
55
+ // Inside is_ok() branch, result should be narrowed to ResultOk
56
+ type _TestNarrowed = Expect<Equal<typeof result, ResultOk<number, string>>>;
57
+ const value: number = result.value;
58
+ }
59
+
60
+ if (result.is_err()) {
61
+ // Inside is_err() branch, result should be narrowed to ResultErr
62
+ type _TestNarrowed = Expect<Equal<typeof result, ResultErr<number, string>>>;
63
+ const error: string = result.value;
64
+ }
65
+ }
66
+
67
+ // --- Map Preserves Types ---
68
+ {
69
+ const ok = Ok(42);
70
+ const mapped = ok.map(x => x.toString());
71
+
72
+ // map on Ok should return ResultOk with new type
73
+ type _TestMapOk = Expect<Equal<typeof mapped, ResultOk<string, never>>>;
74
+
75
+ const err = Err("error");
76
+ const mappedErr = err.map((x: number) => x.toString());
77
+
78
+ // map on Err should return ResultErr with new T type
79
+ type _TestMapErr = Expect<Equal<typeof mappedErr, ResultErr<string, string>>>;
80
+ }
81
+
82
+ // --- MapErr Preserves Types ---
83
+ {
84
+ const ok = Ok(42);
85
+ const mapped = ok.map_err((e: string) => new Error(e));
86
+
87
+ // map_err on Ok should return ResultOk with new E type
88
+ type _TestMapErrOk = Expect<Equal<typeof mapped, ResultOk<number, Error>>>;
89
+
90
+ const err = Err("error");
91
+ const mappedErr = err.map_err(e => new Error(e));
92
+
93
+ // map_err on Err should return ResultErr with new error type
94
+ type _TestMapErrErr = Expect<Equal<typeof mappedErr, ResultErr<never, Error>>>;
95
+ }
96
+
97
+ // --- And/Or Combinators ---
98
+ {
99
+ const ok = Ok(42);
100
+ const err = Err("error");
101
+
102
+ // and should return the argument's type when called on Ok
103
+ const anded = ok.and(Ok("hello"));
104
+ type _TestAnd = Expect<Equal<typeof anded, Result<string, never>>>;
105
+
106
+ // or should return self's type (with new E) when called on Ok
107
+ const ored = ok.or(Err(123));
108
+ type _TestOr = Expect<Equal<typeof ored, ResultOk<number, number>>>;
109
+
110
+ // or should return argument when called on Err
111
+ const errOred = err.or(Ok(42));
112
+ type _TestErrOr = Expect<Equal<typeof errOred, Result<number, never>>>;
113
+ }
114
+
115
+ // --- Transmute ---
116
+ {
117
+ const ok: Result<number, string> = Ok(42);
118
+ const err: Result<number, string> = Err("error");
119
+
120
+ // When we know it's Ok, transmute should give Ok<T, never>
121
+ if (ok.is_ok()) {
122
+ const transmuted = ok.transmute();
123
+ type _TestTransmuteOk = Expect<Equal<typeof transmuted, ResultOk<number, never>>>;
124
+ }
125
+
126
+ // When we know it's Err, transmute should give Err<never, E>
127
+ if (err.is_err()) {
128
+ const transmuted = err.transmute();
129
+ type _TestTransmuteErr = Expect<Equal<typeof transmuted, ResultErr<never, string>>>;
130
+ }
131
+ }
132
+
133
+ // --- Flatten ---
134
+ {
135
+ const nested = Ok(Ok(42));
136
+ const flattened = nested.flatten();
137
+
138
+ // Flatten should unwrap one layer of Result
139
+ type _TestFlatten = Expect<Equal<typeof flattened, Result<number, never>>>;
140
+
141
+ const nestedErr = Ok(Err("inner error"));
142
+ const flattenedErr = nestedErr.flatten();
143
+
144
+ // Flatten should combine error types
145
+ type _TestFlattenErr = Expect<Equal<typeof flattenedErr, Result<never, string>>>;
146
+ }
147
+
148
+ // --- UnwrapOk/UnwrapErr Type Utilities ---
149
+ {
150
+ type TestResult = Result<number, string>;
151
+
152
+ type _TestUnwrapOk = Expect<Equal<UnwrapOk<TestResult>, number>>;
153
+ type _TestUnwrapErr = Expect<Equal<UnwrapErr<TestResult>, string>>;
154
+ }
155
+
156
+ // --- FlattenResult Type Utility ---
157
+ {
158
+ type Nested = Result<Result<number, string>, boolean>;
159
+ type _TestFlattenType = Expect<Equal<FlattenResult<Result<number, string>, boolean>, Result<number, string | boolean>>>;
160
+ }
161
+
162
+ // --- Collect Function ---
163
+ {
164
+ const results = [Ok(1), Ok(2), Ok(3)] as const;
165
+ const collected = collect(results);
166
+
167
+ // Collect should produce Result with tuple type
168
+ type _TestCollect = Expect<Equal<
169
+ typeof collected,
170
+ Result<readonly [number, number, number], never>
171
+ >>;
172
+ }
173
+
174
+ // --- Match Function ---
175
+ {
176
+ const result: Result<number, string> = Ok(42);
177
+ const matched = matchResult(result, {
178
+ Ok: (value) => value * 2,
179
+ Err: (error) => error.length,
180
+ });
181
+
182
+ type _TestMatch = Expect<Equal<typeof matched, number>>;
183
+ }
184
+
185
+ // ============================================================================
186
+ // Option Type-Level Tests
187
+ // ============================================================================
188
+
189
+ // --- Factory Function Types ---
190
+ {
191
+ const some = Some(42);
192
+ const none = None<number>();
193
+
194
+ // Some should produce OptionSome<T>
195
+ type _TestSomeType = Expect<Equal<typeof some, OptionSome<number>>>;
196
+
197
+ // None should produce OptionNone<T>
198
+ type _TestNoneType = Expect<Equal<typeof none, OptionNone<number>>>;
199
+ }
200
+
201
+ // --- Type Guards ---
202
+ {
203
+ const option: Option<number> = Some(42);
204
+
205
+ if (option.is_some()) {
206
+ // Inside is_some() branch, option should be narrowed to OptionSome
207
+ type _TestNarrowed = Expect<Equal<typeof option, OptionSome<number>>>;
208
+ const value: number = option.value;
209
+ }
210
+
211
+ if (option.is_none()) {
212
+ // Inside is_none() branch, option should be narrowed to OptionNone
213
+ type _TestNarrowed = Expect<Equal<typeof option, OptionNone<number>>>;
214
+ }
215
+ }
216
+
217
+ // --- Map Preserves Types ---
218
+ {
219
+ const some = Some(42);
220
+ const mapped = some.map(x => x.toString());
221
+
222
+ // map on Some should return OptionSome with new type
223
+ type _TestMapSome = Expect<Equal<typeof mapped, OptionSome<string>>>;
224
+
225
+ const none = None<number>();
226
+ const mappedNone = none.map(x => x.toString());
227
+
228
+ // map on None should return OptionNone with new type
229
+ type _TestMapNone = Expect<Equal<typeof mappedNone, OptionNone<string>>>;
230
+ }
231
+
232
+ // --- And/Or Combinators ---
233
+ {
234
+ const some = Some(42);
235
+ const none = None<number>();
236
+
237
+ // and should return the argument's type when called on Some
238
+ const anded = some.and(Some("hello"));
239
+ type _TestAnd = Expect<Equal<typeof anded, Option<string>>>;
240
+
241
+ // or should return self when called on Some
242
+ const ored = some.or(Some(0));
243
+ type _TestOr = Expect<Equal<typeof ored, OptionSome<number>>>;
244
+
245
+ // or should return argument when called on None
246
+ const noneOred = none.or(Some(0));
247
+ type _TestNoneOr = Expect<Equal<typeof noneOred, Option<number>>>;
248
+ }
249
+
250
+ // --- Flatten ---
251
+ {
252
+ const nested = Some(Some(42));
253
+ const flattened = nested.flatten();
254
+
255
+ // Flatten should unwrap one layer of Option
256
+ type _TestFlatten = Expect<Equal<typeof flattened, Option<number>>>;
257
+ }
258
+
259
+ // --- UnwrapOption Type Utility ---
260
+ {
261
+ type TestOption = Option<number>;
262
+ type _TestUnwrapOption = Expect<Equal<UnwrapOption<TestOption>, number>>;
263
+ }
264
+
265
+ // --- FlattenOption Type Utility ---
266
+ {
267
+ type Nested = Option<Option<number>>;
268
+ type _TestFlattenType = Expect<Equal<FlattenOption<Option<number>>, Option<number>>>;
269
+ }
270
+
271
+ // --- Match Function ---
272
+ {
273
+ const option: Option<number> = Some(42);
274
+ const matched = matchOption(option, {
275
+ Some: (value) => value * 2,
276
+ None: () => 0,
277
+ });
278
+
279
+ type _TestMatch = Expect<Equal<typeof matched, number>>;
280
+ }
281
+
282
+ // --- Zip ---
283
+ {
284
+ const some1 = Some(42);
285
+ const some2 = Some("hello");
286
+ const zipped = some1.zip(some2);
287
+
288
+ type _TestZip = Expect<Equal<typeof zipped, Option<[number, string]>>>;
289
+ }
290
+
291
+ // ============================================================================
292
+ // Variance Tests
293
+ // ============================================================================
294
+
295
+ // Result should be covariant in both T and E
296
+ {
297
+ type Animal = { name: string };
298
+ type Dog = Animal & { breed: string };
299
+
300
+ // ResultOk<Dog, never> should be assignable to ResultOk<Animal, never>
301
+ const dogResult: ResultOk<Dog, never> = Ok({ name: "Rex", breed: "German Shepherd" });
302
+ const animalResult: ResultOk<Animal, never> = dogResult; // Should compile
303
+
304
+ // ResultErr<never, Dog> should be assignable to ResultErr<never, Animal>
305
+ // (if we consider errors as covariant too)
306
+ }
307
+
308
+ // Option should be covariant in T
309
+ {
310
+ type Animal = { name: string };
311
+ type Dog = Animal & { breed: string };
312
+
313
+ // OptionSome<Dog> should be assignable to OptionSome<Animal>
314
+ const dogOption: OptionSome<Dog> = Some({ name: "Rex", breed: "German Shepherd" });
315
+ const animalOption: OptionSome<Animal> = dogOption; // Should compile
316
+ }
317
+
318
+ // ============================================================================
319
+ // Error Cases (These should fail to compile)
320
+ // ============================================================================
321
+
322
+ // Uncomment to verify these correctly fail:
323
+
324
+ // @ts-expect-error - Cannot unwrap Err without checking
325
+ // const _err1: number = Err("error").unwrap();
326
+
327
+ // @ts-expect-error - Cannot unwrap None without checking
328
+ // const _err2: number = None<number>().unwrap();
329
+
330
+ // @ts-expect-error - is_ok_and on Err always returns false literal
331
+ // const _err3: true = Err("error").is_ok_and(() => true);
332
+
333
+ // @ts-expect-error - is_some_and on None always returns false literal
334
+ // const _err4: true = None<number>().is_some_and(() => true);
335
+
336
+ // Actual runtime test to satisfy Jest
337
+ describe('Type-level tests', () => {
338
+ it('should compile without errors', () => {
339
+ // If this file compiles, all type tests passed
340
+ expect(true).toBe(true);
341
+ });
342
+
343
+ it('Ok factory returns ResultOk<T, never>', () => {
344
+ const ok = Ok(42);
345
+ expect(ok._tag).toBe('Ok');
346
+ expect(ok.value).toBe(42);
347
+ });
348
+
349
+ it('Err factory returns ResultErr<never, E>', () => {
350
+ const err = Err("error");
351
+ expect(err._tag).toBe('Err');
352
+ expect(err.value).toBe("error");
353
+ });
354
+
355
+ it('Some factory returns OptionSome<T>', () => {
356
+ const some = Some(42);
357
+ expect(some._tag).toBe('Some');
358
+ expect(some.value).toBe(42);
359
+ });
360
+
361
+ it('None factory returns OptionNone<T>', () => {
362
+ const none = None<number>();
363
+ expect(none._tag).toBe('None');
364
+ });
365
+
366
+ it('transmute on Ok returns Ok with never error type', () => {
367
+ const ok = Ok(42);
368
+ const transmuted = ok.transmute();
369
+ expect(transmuted._tag).toBe('Ok');
370
+ expect(transmuted.value).toBe(42);
371
+ });
372
+
373
+ it('transmute on Err returns Err with never value type', () => {
374
+ const err = Err("error");
375
+ const transmuted = err.transmute();
376
+ expect(transmuted._tag).toBe('Err');
377
+ expect(transmuted.value).toBe("error");
378
+ });
379
+
380
+ it('collect combines Ok results into array', () => {
381
+ const results = [Ok(1), Ok(2), Ok(3)] as const;
382
+ const collected = collect(results);
383
+ expect(collected.is_ok()).toBe(true);
384
+ expect(collected.unwrap()).toEqual([1, 2, 3]);
385
+ });
386
+
387
+ it('collect returns first Err', () => {
388
+ const results = [Ok(1), Err("fail"), Ok(3)] as const;
389
+ const collected = collect(results);
390
+ expect(collected.is_err()).toBe(true);
391
+ expect(collected.unwrap_err()).toBe("fail");
392
+ });
393
+
394
+ it('match on Result works correctly', () => {
395
+ const ok: Result<number, string> = Ok(42);
396
+ const err: Result<number, string> = Err("error");
397
+
398
+ expect(matchResult(ok, { Ok: v => v * 2, Err: e => e.length })).toBe(84);
399
+ expect(matchResult(err, { Ok: v => v * 2, Err: e => e.length })).toBe(5);
400
+ });
401
+
402
+ it('match on Option works correctly', () => {
403
+ const some: Option<number> = Some(42);
404
+ const none: Option<number> = None();
405
+
406
+ expect(matchOption(some, { Some: v => v * 2, None: () => 0 })).toBe(84);
407
+ expect(matchOption(none, { Some: v => v * 2, None: () => 0 })).toBe(0);
408
+ });
409
+ });
package/dist/lib.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export * from './result';
2
- export * from './option';
package/dist/lib.js DELETED
@@ -1,19 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./result"), exports);
18
- __exportStar(require("./option"), exports);
19
- //# sourceMappingURL=lib.js.map
package/dist/lib.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"lib.js","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAyB;AACzB,2CAAyB"}