Skip to content

Commit 136ffbe

Browse files
committed
chore: add type-only tests
1 parent e2c8663 commit 136ffbe

1 file changed

Lines changed: 148 additions & 0 deletions

File tree

tests/types.test-d.ts

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import { describe, test, expectTypeOf, assertType } from 'vitest';
2+
import {
3+
Some,
4+
None,
5+
type Option,
6+
type SomeOption,
7+
type NoneOption
8+
} from '../src/option';
9+
import {
10+
Ok,
11+
Err,
12+
type Result,
13+
type OkResult,
14+
type ErrResult
15+
} from '../src/result';
16+
import { type AsyncOption } from '../src/async-option';
17+
import { type AsyncResult } from '../src/async-result';
18+
19+
describe('Option types', () => {
20+
test('Some and None constructors map correctly to Option<T>', () => {
21+
const someValue = Some(42);
22+
const noneValue = None<number>();
23+
24+
expectTypeOf(someValue).toEqualTypeOf<Option<number>>();
25+
expectTypeOf(noneValue).toEqualTypeOf<Option<number>>();
26+
27+
// @ts-expect-error - NoneOption cannot be assigned to SomeOption
28+
const strictSome: SomeOption<number> = None<number>();
29+
});
30+
31+
test('isSome and isNone type guard refinements', () => {
32+
const opt = Some(42) as Option<number>;
33+
34+
if (opt.isSome()) expectTypeOf(opt).toEqualTypeOf<SomeOption<number>>();
35+
else if (opt.isNone())
36+
expectTypeOf(opt).toEqualTypeOf<NoneOption<number>>();
37+
else expectTypeOf(opt).toEqualTypeOf<never>();
38+
});
39+
40+
test('isSomeAnd performs user-defined type-guard narrowing', () => {
41+
const opt = Some<string | number>(42);
42+
const isString = (val: string | number): val is string =>
43+
typeof val === 'string';
44+
45+
if (opt.isSomeAnd(isString))
46+
expectTypeOf(opt).toEqualTypeOf<SomeOption<string>>();
47+
});
48+
49+
test('Chaining methods (map, mapAsync, andThen, okOr)', () => {
50+
const opt = Some(42);
51+
52+
expectTypeOf(opt.map((v) => v.toString())).toEqualTypeOf<
53+
Option<string>
54+
>();
55+
56+
expectTypeOf(opt.mapAsync(async (v) => v.toString())).toEqualTypeOf<
57+
AsyncOption<string>
58+
>();
59+
60+
expectTypeOf(opt.andThen((v) => Some(v > 0))).toEqualTypeOf<
61+
Option<boolean>
62+
>();
63+
64+
expectTypeOf(opt.okOr('error_msg')).toEqualTypeOf<
65+
Result<number, string>
66+
>();
67+
});
68+
});
69+
70+
describe('Result types', () => {
71+
test('Ok and Err map into variants of Result<T, E>', () => {
72+
const okVal = Ok(100);
73+
const errVal = Err('failed');
74+
75+
expectTypeOf(okVal).toEqualTypeOf<Result<number, never>>();
76+
expectTypeOf(errVal).toEqualTypeOf<Result<never, string>>();
77+
78+
assertType<Result<number, string>>(okVal);
79+
assertType<Result<number, string>>(errVal);
80+
});
81+
82+
test('isOk and isErr type guards split types correctly', () => {
83+
const res = Ok(42) as Result<number, string>;
84+
85+
if (res.isOk())
86+
expectTypeOf(res).toEqualTypeOf<OkResult<number, never>>();
87+
else if (res.isErr())
88+
expectTypeOf(res).toEqualTypeOf<ErrResult<never, string>>();
89+
});
90+
91+
test('isOkAnd user-defined type guard narrowing', () => {
92+
const res = Ok<string | number>(42) as Result<string | number, string>;
93+
const isNumber = (val: string | number): val is number =>
94+
typeof val === 'number';
95+
96+
if (res.isOkAnd(isNumber))
97+
expectTypeOf(res).toEqualTypeOf<OkResult<number, string>>();
98+
});
99+
100+
test('Chaining operations (map, mapErr, mapAsync, ok, err)', () => {
101+
const res = Ok(42) as Result<number, string>;
102+
103+
expectTypeOf(res.map((v) => v * 2)).toEqualTypeOf<
104+
Result<number, string>
105+
>();
106+
107+
expectTypeOf(res.mapErr((e) => new Error(e))).toEqualTypeOf<
108+
Result<number, Error>
109+
>();
110+
111+
expectTypeOf(res.mapAsync(async (v) => v.toString())).toEqualTypeOf<
112+
AsyncResult<string, string>
113+
>();
114+
115+
expectTypeOf(res.ok()).toEqualTypeOf<Option<number>>();
116+
expectTypeOf(res.err()).toEqualTypeOf<Option<string>>();
117+
});
118+
});
119+
120+
describe('Async Wrappers (AsyncOption & AsyncResult)', () => {
121+
test('AsyncOption promise-like structural behavior', () => {
122+
const asyncOpt = {} as AsyncOption<number>;
123+
124+
expectTypeOf(asyncOpt).toExtend<PromiseLike<Option<number>>>();
125+
126+
expectTypeOf(asyncOpt.isSome()).toEqualTypeOf<Promise<boolean>>();
127+
expectTypeOf(asyncOpt.unwrap()).toEqualTypeOf<Promise<number>>();
128+
expectTypeOf(asyncOpt.map((v) => v.toString())).toEqualTypeOf<
129+
AsyncOption<string>
130+
>();
131+
expectTypeOf(asyncOpt.okOr('err')).toEqualTypeOf<
132+
AsyncResult<number, string>
133+
>();
134+
});
135+
136+
test('AsyncResult promise-like structural behavior', () => {
137+
const asyncRes = {} as AsyncResult<number, string>;
138+
139+
expectTypeOf(asyncRes).toExtend<PromiseLike<Result<number, string>>>();
140+
141+
expectTypeOf(asyncRes.isOk()).toEqualTypeOf<Promise<boolean>>();
142+
expectTypeOf(asyncRes.map((v) => v * 2)).toEqualTypeOf<
143+
AsyncResult<number, string>
144+
>();
145+
expectTypeOf(asyncRes.ok()).toEqualTypeOf<AsyncOption<number>>();
146+
expectTypeOf(asyncRes.err()).toEqualTypeOf<AsyncOption<string>>();
147+
});
148+
});

0 commit comments

Comments
 (0)