Skip to content

Commit 0b3d4b4

Browse files
code
1 parent 90d799f commit 0b3d4b4

File tree

1 file changed

+84
-70
lines changed

1 file changed

+84
-70
lines changed

README.md

Lines changed: 84 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ Only the `catch (error) {}` block represents actual control flow, while no progr
2121
<br />
2222

2323
- [Try/Catch Is Not Enough](#trycatch-is-not-enough)
24+
- [Caller's Approach](#callers-approach)
2425
- [What This Proposal Does Not Aim to Solve](#what-this-proposal-does-not-aim-to-solve)
26+
- [Strict Type Enforcement for Errors](#strict-type-enforcement-for-errors)
27+
- [Automatic Error Handling](#automatic-error-handling)
2528
- [Try Operator](#try-operator)
2629
- [Expressions are evaluated in a self-contained `try/catch` block](#expressions-are-evaluated-in-a-self-contained-trycatch-block)
2730
- [Can be inlined.](#can-be-inlined)
@@ -31,10 +34,12 @@ Only the `catch (error) {}` block represents actual control flow, while no progr
3134
- [Never throws](#never-throws)
3235
- [Parenthesis Required for Object Literals](#parenthesis-required-for-object-literals)
3336
- [Void Operations](#void-operations)
34-
- [Result class](#result-class)
37+
- [Result Class](#result-class)
38+
- [Instance Structure](#instance-structure)
39+
- [Iterable](#iterable)
40+
- [Manual Creation](#manual-creation)
3541
- [Why Not `data` First?](#why-not-data-first)
3642
- [The Need for an `ok` Value](#the-need-for-an-ok-value)
37-
- [Caller's Approach](#callers-approach)
3843
- [Why a Proposal?](#why-a-proposal)
3944
- [Help Us Improve This Proposal](#help-us-improve-this-proposal)
4045
- [Authors](#authors)
@@ -124,11 +129,55 @@ A `try` statement provide significant flexibility and arguably result in more re
124129

125130
<br />
126131

132+
## Caller's Approach
133+
134+
JavaScript has evolved over decades, with countless libraries and codebases built on top of one another. Any new feature that does not consider compatibility with existing code risks negatively impacting its adoption, as refactoring functional, legacy code simply to accommodate a new feature is often an unjustifiable cost.
135+
136+
With that in mind, improvements in error handling can be approached in two ways:
137+
138+
1. **At the caller's level**:
139+
140+
```js
141+
try {
142+
const result = work()
143+
} catch (error) {
144+
console.error(error)
145+
}
146+
```
147+
148+
2. **At the callee's level**:
149+
150+
```js
151+
function work() {
152+
// Performs some operation
153+
154+
if (error) {
155+
return { status: "error", error }
156+
} else {
157+
return { status: "ok", data }
158+
}
159+
}
160+
```
161+
162+
Both approaches achieve the same goal, but the second one requires refactoring all implementations into a new format. This is how languages like Go and Rust handle errors, returning a tuple of an error and a value or a `Result` object, respectively. While the callee-based approach can arguably be better, it succeeded in those languages because it was adopted from the very beginning, rather than introduced as a later addition.
163+
164+
This proposal accounts for this by moving the transformation of errors into values to the **caller** level, preserving the familiar semantics and placement of `try/catch`. This approach ensures backward compatibility with existing code.
165+
166+
Breaking compatibility is unacceptable for platforms like Node.js or libraries. Consequently, a callee-based approach would likely never be adopted for functions like `fetch` or `fs.readFile`, as it would disrupt existing codebases. Ironically, these are precisely the kinds of functions where improved error handling is most needed.
167+
168+
<br />
169+
127170
## What This Proposal Does Not Aim to Solve
128171

129-
1. **Strict Type Enforcement for Errors**: The `throw` statement in JavaScript can throw any type of value. This proposal does not impose type safety on error handling and will not introduce types into the language. For more information, see [microsoft/typescript#13219](https://github.com/Microsoft/TypeScript/issues/13219). _(This also means no generic error type for [Result](#result-class))_
172+
### Strict Type Enforcement for Errors
173+
174+
The `throw` statement in JavaScript can throw any type of value. This proposal does not impose nor proposes any kind safety on error handling.
175+
176+
For more information, see [microsoft/typescript#13219](https://github.com/Microsoft/TypeScript/issues/13219). _(This also means no generic error type for the proposed [Result](#result-class) class)_
177+
178+
### Automatic Error Handling
130179

131-
2. **Automatic Error Handling**: While this proposal facilitates error handling, it does not automatically handle errors for you. You will still need to write the necessary code to manage errors the proposal simply aims to make this process easier and more consistent.
180+
While this proposal facilitates error handling, it does not automatically handle errors for you. You will still need to write the necessary code to manage errors the proposal simply aims to make this process easier and more consistent.
132181

133182
<br />
134183

@@ -304,48 +353,51 @@ function work() {
304353
305354
<br />
306355
307-
## Result class
356+
## Result Class
308357
309358
> Please see [`polyfill.d.ts`](./polyfill.d.ts) and [`polyfill.js`](./polyfill.js) for a basic implementation of the `Result` class.
310359
311360
The `Result` class represents the form of the value returned by the `try` operator.
312361
313-
1. **Structure of a `Result` Instance**
314-
A `Result` instance contains three properties:
362+
### Instance Structure
315363
316-
- **`ok`**: A boolean indicating whether the expression executed successfully.
317-
- **`error`**: The error thrown during execution, or `undefined` if no error occurred.
318-
- **`value`**: The data returned from the execution, or `undefined` if an error occurred.
364+
A `Result` instance contains three properties:
319365
320-
Example usage:
366+
- **`ok`**: A boolean indicating whether the expression executed successfully.
367+
- **`error`**: The error thrown during execution, or `undefined` if no error occurred.
368+
- **`value`**: The data returned from the execution, or `undefined` if an error occurred.
321369
322-
```js
323-
const result = try something()
370+
Example usage:
324371
325-
if (result.ok) {
326-
console.log(result.value)
327-
} else {
328-
console.error(result.error)
329-
}
330-
```
372+
```js
373+
const result = try something()
374+
375+
if (result.ok) {
376+
console.log(result.value)
377+
} else {
378+
console.error(result.error)
379+
}
380+
```
331381
332-
2. **Iterable Behavior**
333-
A `Result` instance is iterable, enabling destructuring and different variable names:
382+
### Iterable
334383
335-
```js
336-
const [success, validationError, user] = try User.parse(myJson)
337-
```
384+
A `Result` instance is iterable, enabling destructuring and different variable names:
338385
339-
3. **Manual Creation of a `Result`**
340-
You can also create a `Result` instance manually using its constructor or static methods:
386+
```js
387+
const [success, validationError, user] = try User.parse(myJson)
388+
```
341389
342-
```js
343-
// Creating a successful result
344-
const result = Result.ok(value)
390+
### Manual Creation
345391
346-
// Creating an error result
347-
const result = Result.error(error)
348-
```
392+
You can also create a `Result` instance manually using its constructor or static methods:
393+
394+
```js
395+
// Creating a successful result
396+
const result = Result.ok(value)
397+
398+
// Creating an error result
399+
const result = Result.error(error)
400+
```
349401
350402
<br />
351403
@@ -421,44 +473,6 @@ For a more in-depth explanation of this decision, refer to [GitHub Issue #30](ht
421473
422474
<br />
423475
424-
## Caller's Approach
425-
426-
JavaScript has evolved over decades, with countless libraries and codebases built on top of one another. Any new feature that does not consider compatibility with existing code risks negatively impacting its adoption, as refactoring functional, legacy code simply to accommodate a new feature is often an unjustifiable cost.
427-
428-
With that in mind, improvements in error handling can be approached in two ways:
429-
430-
1. **At the caller's level**:
431-
432-
```js
433-
try {
434-
const result = work()
435-
} catch (error) {
436-
console.error(error)
437-
}
438-
```
439-
440-
2. **At the callee's level**:
441-
442-
```js
443-
function work() {
444-
// Performs some operation
445-
446-
if (error) {
447-
return { status: "error", error }
448-
} else {
449-
return { status: "ok", data }
450-
}
451-
}
452-
```
453-
454-
Both approaches achieve the same goal, but the second one requires refactoring all implementations into a new format. This is how languages like Go and Rust handle errors, returning a tuple of an error and a value or a `Result` object, respectively. While the callee-based approach can arguably be better, it succeeded in those languages because it was adopted from the very beginning, rather than introduced as a later addition.
455-
456-
This proposal accounts for this by moving the transformation of errors into values to the **caller** level, preserving the familiar semantics and placement of `try/catch`. This approach ensures backward compatibility with existing code.
457-
458-
Breaking compatibility is unacceptable for platforms like Node.js or libraries. Consequently, a callee-based approach would likely never be adopted for functions like `fetch` or `fs.readFile`, as it would disrupt existing codebases. Ironically, these are precisely the kinds of functions where improved error handling is most needed.
459-
460-
<br />
461-
462476
## Why a Proposal?
463477
464478
A proposal doesn’t need to introduce a feature that is entirely impossible to achieve otherwise. In fact, most recent proposals primarily reduce the complexity of tasks that are already achievable by providing built-in conveniences.

0 commit comments

Comments
 (0)