You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+90-59Lines changed: 90 additions & 59 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,8 +2,10 @@
2
2
3
3
<h1>ECMAScript Try Operator</h1>
4
4
5
-
> [!WARNING]
6
-
> After extensive discussion and feedback, the proposal was renamed from `Safe Assignment Operator` to `Try Operator`. _Click here to view the [original proposal](https://github.com/arthurfiorette/proposal-try-operator/tree/old/proposal-safe-assignment-operator)._
5
+
> [!TIP]
6
+
> You can test the runtime aspect of this proposal and its ergonomics today! Install our reference `Result` class implementation from NPM:
@@ -17,40 +19,40 @@ This proposal addresses the ergonomic challenges of managing multiple, often nes
17
19
18
20
Only the `catch (error) {}` block represents actual control flow, while no program state inherently depends on being inside a `try {}` block. Therefore, forcing the successful flow into nested blocks is not ideal.
19
21
20
-
<details>
21
-
22
-
<summary>
23
-
<h2>Table of Contents</h2>
24
-
</summary>
22
+
<br />
25
23
26
24
-[Status](#status)
27
25
-[Authors](#authors)
28
26
-[Try/Catch Is Not Enough](#trycatch-is-not-enough)
29
27
-[Caller's Approach](#callers-approach)
30
-
-[What This Proposal Does Not Aim to Solve](#what-this-proposal-does-not-aim-to-solve)
-[The Need for an `ok` Value](#the-need-for-an-ok-value)
48
-
-[Why a Proposal?](#why-a-proposal)
49
+
-[A Case for Syntax](#a-case-for-syntax)
50
+
-[Why This Belongs in the Language](#why-this-belongs-in-the-language)
49
51
-[Help Us Improve This Proposal](#help-us-improve-this-proposal)
50
52
-[Inspiration](#inspiration)
51
53
-[License](#license)
52
54
53
-
</details>
55
+
<br />
54
56
55
57
## Status
56
58
@@ -70,7 +72,7 @@ _For more information see the [TC39 proposal process](https://tc39.es/process-do
70
72
71
73
## Try/Catch Is Not Enough
72
74
73
-
<!-- Credits to https://x.com/LeaVerou/status/1819381809773216099 :) -->
75
+
<!-- Credits to https://x.com/LeaVerou/status/1819381809773216099 -->
74
76
75
77
The `try {}` block often feels redundant because its scoping lacks meaningful conceptual significance. Rather than serving as an essential control flow construct, it mostly acts as a code annotation. Unlike loops or conditionals, a `try {}` block doesn’t encapsulate any distinct program state that requires isolation.
76
78
@@ -212,24 +214,6 @@ Ironically, **these are precisely the kinds of functions where improved error ha
212
214
213
215
<br />
214
216
215
-
## What This Proposal Does Not Aim to Solve
216
-
217
-
### Type-Safe Errors
218
-
219
-
The `throw` statement in JavaScript can throw any type of value. This proposal does not impose nor propose any kind of safety around error handling.
220
-
221
-
- No generic error type for the proposed [Result](#result-class) class will be added.
222
-
- No catch branching based on error type will be added. See [GitHub Issue #43](https://github.com/arthurfiorette/proposal-try-operator/issues/43) for more information.
223
-
- No way to annotate a callable to specify the error type it throws will be added.
224
-
225
-
For more information, also see [microsoft/typescript#13219](https://github.com/Microsoft/TypeScript/issues/13219).
226
-
227
-
### Automatic Error Handling
228
-
229
-
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.
230
-
231
-
<br />
232
-
233
217
## Try Operator
234
218
235
219
The `try` operator consists of the `try` keyword followed by an expression. It results in an instance of the [`Result`](#result-class).
@@ -278,7 +262,7 @@ const result = _result
278
262
Similar to `void`, `typeof`, `yield`, and `new`:
279
263
280
264
```js
281
-
array.map((fn) =>tryfn()).filter((result) =>result.ok)// works :)
@@ -369,7 +353,7 @@ This behavior mirrors how JavaScript differentiates blocks and object literals:
369
353
370
354
<!-- prettier-ignore -->
371
355
```js
372
-
{ a:1 } // empty block with a label
356
+
{ a:1 } // empty block with a label
373
357
({ a:1 }) // object with a key `a` and a number `1`
374
358
```
375
359
@@ -404,19 +388,28 @@ function work() {
404
388
405
389
## Result Class
406
390
407
-
> Please see [`polyfill.d.ts`](./polyfill.d.ts) and [`polyfill.js`](./polyfill.js) for a basic implementation of the `Result` class.
391
+
The `try` operator evaluates an expression and returns an instance of the `Result` class, which encapsulates the outcome of the operation.
408
392
409
-
The `Result` class represents the form of the value returned by the `try` operator.
393
+
### Reference Implementation
394
+
395
+
To validate the ergonomics and utility of this proposal, a spec-compliant, runtime-only implementation of the `Result` class has been published to npm as the [`try`](https://www.npmjs.com/package/try) package. This package provides a `t()` function that serves as a polyfill for the `try` operator's runtime behavior, allowing developers to experiment with the core pattern.
You can check the published package at [npmjs.com/package/try](https://www.npmjs.com/package/try) or [github.com/arthurfiorette/try](https://github.com/arthurfiorette/try) and contribute to its development.
410
404
411
405
### Instance Structure
412
406
413
-
A `Result` instance contains three properties:
407
+
A `Result` instance always contains a boolean `ok` property that indicates the outcome.
414
408
415
-
- **`ok`**: A boolean indicating whether the expression was executed successfully.
416
-
- **`error`**: The error thrown during execution, or `undefined` if no error occurred.
417
-
- **`value`**: The data returned from the execution, or `undefined` if an error occurred.
409
+
- If `ok` is `true`, the instance also has a `value` property containing the successful result.
410
+
- If `ok` is `false`, it has an `error` property containing the thrown exception.
418
411
419
-
Example usage:
412
+
Crucially, a success result does not have an `error` property, and a failure result does not have a `value` property. This allows for reliable checks like `'error'in result`.
420
413
421
414
```js
422
415
constresult=trysomething()
@@ -428,26 +421,54 @@ if (result.ok) {
428
421
}
429
422
```
430
423
431
-
### Iterable
424
+
### Iterable Protocol
432
425
433
-
A `Result`instance is iterable, enabling destructuring and different variable names:
426
+
To support ergonomic destructuring, `Result`instances are iterable. They yield their state in the order `[ok, error, value]`, allowing for clear, inline handling of both success and failure cases.
You can also create a `Result`instance manually using its constructor or static methods:
434
+
While the `try` operator is the primary source of `Result`instances, they can also be created manually using static methods. This is useful for testing or for bridging with APIs that do not use exceptions.
It also includes a static `Result.try()` method, which serves as the runtime foundation for the `try` operator. This method wraps a function call, catching any synchronous exceptions or asynchronous rejections and returning a `Result` or `Promise<Result>`, respectively.
447
+
448
+
The proposed `try expression` syntax is essentially an ergonomic improvement over the more verbose `Result.try(() => expression)`, removing the need for a function wrapper.
449
+
450
+
### No Result Flattening
451
+
452
+
The `try` operator and `Result` constructors wrap the value they are given without inspection. If this value is itself a `Result` instance, it will be nested, not flattened. This ensures predictable and consistent behavior.
453
+
454
+
<br />
455
+
456
+
## What This Proposal Does Not Aim to Solve
457
+
458
+
### Type-Safe Errors
459
+
460
+
The `throw` statement in JavaScript can throw any type of value. This proposal does not impose nor propose any kind of safety around error handling.
461
+
462
+
- No generic error type for the proposed [Result](#result-class) class will be added.
463
+
- No catch branching based on error type will be added. See [GitHub Issue #43](https://github.com/arthurfiorette/proposal-try-operator/issues/43) for more information.
464
+
- No way to annotate a callable to specify the error type it throws will be added.
465
+
466
+
For more information, also see [microsoft/typescript#13219](https://github.com/Microsoft/TypeScript/issues/13219).
467
+
468
+
### Automatic Error Handling
469
+
470
+
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.
471
+
451
472
<br />
452
473
453
474
## Why Not `data` First?
@@ -522,16 +543,25 @@ For a more in-depth explanation of this decision, refer to [GitHub Issue #30](ht
522
543
523
544
<br />
524
545
525
-
## Why a Proposal?
546
+
## A Case for Syntax
547
+
548
+
This proposal intentionally combines the `try` operator with the `Result` class because one is incomplete without the other. The `try` operator standardizes the many attempts at safely catching synchronous function calls (the way we can with Promise `.catch` for async calls). Consistency is key, and the `try` syntax establishes common patterns for all developers.
549
+
550
+
It has been suggested that a runtime-only proposal for the `Result` class might face less resistance within the TC39 process. While this strategic viewpoint is understood, this proposal deliberately presents a unified feature. Separating the runtime from the syntax severs the solution from its motivating problem. It would ask the committee to standardize a `Result` object whose design is justified by a syntax **that doesn't yet exist**.
551
+
552
+
Without the `try` operator, the `Result` class is just one of many possible library implementations, not a definitive language feature. We believe the feature must be evaluated on its complete ergonomic and practical merits, which is only possible when the syntax and runtime are presented together.
553
+
554
+
<br />
555
+
556
+
## Why This Belongs in the Language
526
557
527
558
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.
528
559
529
-
Optional chaining and nullish coalescing are examples of features that could have remained external libraries (e.g., Lodash's `_.get()` for optional chaining and `_.defaultTo()` for nullish coalescing). However, when implemented natively, their usage scales exponentially and becomes a natural part of developers’ workflows. This arguably improves code quality and productivity.
560
+
The absence of a `Result`-like type and a standard pattern for safely wrapping function calls has led to widespread ecosystem fragmentation. The NPM registry contains hundreds of variations attempting to implement safe wrapping of function calls, and countless more exist as private, copy-pasted utilities. This leaves developers with a poor choice: risk adopting a library that may be abandoned, or contribute to the problem by creating yet another bespoke implementation.
530
561
531
-
By providing such basic conveniences natively, we:
562
+
This is the same problem that optional chaining (`?.`) and nullish coalescing (`??`) solved. **They replaced a landscape of competing utilities with a single, trusted language feature**. By standardizing this pattern, we provide a reliable primitive that developers can use with confidence, knowing it is a stable and permanent part of JavaScript.
532
563
533
-
- Increase consistency across codebases (many NPM packages already implement variations of this proposal, each with its own API and lack of standardization).
534
-
- Reduce code complexity, making it more readable and less error-prone.
564
+
It also creates a shared foundation between developers and package authors. Everyone can rely on the same Result implementation without compatibility concerns. The goal is to end the fragmentation and establish a foundational tool for robust error handling.
535
565
536
566
<br />
537
567
@@ -548,7 +578,8 @@ This proposal is in its early stages, and we welcome your input to help refine i
548
578
- [This tweet from @LeaVerou](https://x.com/LeaVerou/status/1819381809773216099)
549
579
- The frequent oversight of error handling in JavaScript code.
- The [`tuple-it`](https://www.npmjs.com/package/tuple-it) npm package, which introduces a similar concept but modifies the `Promise` and `Function` prototypes—an approach that is less ideal.
581
+
- The [`tuple-it`](https://www.npmjs.com/package/tuple-it) npm package, which introduces a similar concept but modifies the `Promise` and `Function` prototypes.
582
+
- [Szymon Wygnański](https://finalclass.net) for donating the `try` package name on NPM to host the reference implementation of this proposal.
0 commit comments