-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathresult.ts
More file actions
147 lines (131 loc) · 3.8 KB
/
Copy pathresult.ts
File metadata and controls
147 lines (131 loc) · 3.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/**
* Generic Result type for representing success or failure outcomes.
*
* This is the standard way to return "expected failures" as values rather than
* throwing exceptions. See docs/Error Handling.md for the full taxonomy.
*
* Naming rationale:
* - `Ok<T>` / `NotOk<F>` mirror the `ok: true/false` discriminator
* - `NotOk` avoids collision with domain types like "Failure" or "Error"
* - `failure` property distinguishes from JS Error semantics
*/
import { blindCast } from './casts';
import { InternalError } from './internal-error';
/**
* Represents a successful result containing a value.
*/
export interface Ok<T> {
readonly ok: true;
readonly value: T;
assertOk(): T;
assertNotOk(): never;
}
/**
* Represents an unsuccessful result containing failure details.
*/
export interface NotOk<F> {
readonly ok: false;
readonly failure: F;
assertOk(): never;
assertNotOk(): F;
}
/**
* A discriminated union representing either success (Ok) or failure (NotOk).
*
* @typeParam T - The success value type
* @typeParam F - The failure details type
*/
export type Result<T, F> = Ok<T> | NotOk<F>;
/**
* Result class that implements both Ok and NotOk variants.
*/
class ResultImpl<T, F> {
readonly ok: boolean;
private readonly _value?: T;
private readonly _failure?: F;
private constructor(ok: boolean, valueOrFailure: T | F) {
this.ok = ok;
if (ok) {
this._value = valueOrFailure as T;
} else {
this._failure = valueOrFailure as F;
}
Object.freeze(this);
}
get value(): T {
if (!this.ok) {
throw new InternalError('Cannot access value on NotOk result');
}
// biome-ignore lint/style/noNonNullAssertion: must be present if ok is true
return this._value!;
}
get failure(): F {
if (this.ok) {
throw new InternalError('Cannot access failure on Ok result');
}
// biome-ignore lint/style/noNonNullAssertion: must be present if ok is false
return this._failure!;
}
/**
* Creates a successful result.
*/
static ok<T, F = never>(value: T): Ok<T> {
return blindCast<
Ok<T>,
'ResultImpl is the single implementation of the Result discriminated union; TypeScript cannot express discriminated return types for a single class. ok=true guarantees this is an Ok<T> at runtime.'
>(new ResultImpl<T, F>(true, value));
}
/**
* Creates an unsuccessful result.
*/
static notOk<T = never, F = unknown>(failure: F): NotOk<F> {
return blindCast<
NotOk<F>,
'ResultImpl is the single implementation of the Result discriminated union; TypeScript cannot express discriminated return types for a single class. ok=false guarantees this is a NotOk<F> at runtime.'
>(new ResultImpl<T, F>(false, failure));
}
/**
* Asserts that this result is Ok and returns the value.
* Throws if the result is NotOk.
*/
assertOk(this: Result<T, F>): T {
if (!this.ok) {
throw new InternalError('Expected Ok result but got NotOk');
}
return this.value;
}
/**
* Asserts that this result is NotOk and returns the failure.
* Throws if the result is Ok.
*/
assertNotOk(this: Result<T, F>): F {
if (this.ok) {
throw new InternalError('Expected NotOk result but got Ok');
}
return this.failure;
}
}
/**
* Creates a successful result.
*/
export function ok<T>(value: T): Ok<T> {
return ResultImpl.ok(value);
}
/**
* Creates an unsuccessful result.
*/
export function notOk<F>(failure: F): NotOk<F> {
return ResultImpl.notOk(failure);
}
/**
* Singleton for void success results.
* Use this for validation checks that don't produce a value.
*/
const OK_VOID: Ok<void> = ResultImpl.ok<void>(undefined);
/**
* Returns a successful void result.
* Use this for validation checks that don't produce a value.
*/
export function okVoid(): Ok<void> {
return OK_VOID;
}