the success value type
the failure error type
You might be familiar with the Either
type from functional programming. The Result
type
could be seen as a more specific version of Either
where the left side is reserved for
success scenarios and the right side is reserved for known failure scenarios.
Think of failure scenarios as alternative outcomes of a given task that although not the "happy path", are still legitimate results for the task within the boundary of a correct usage of the SDK.
In promoting exhaustive error handling, the Lens SDK makes it easier to evolve your code
when a new error case is added or a case is removed.
For example after a Lens SDK upgrade you can simply run the TS compiler to figure out where you
need to handle the new error cases, or even better, it guides you to remove obsolescent code
where an error case is no longer possible. This is virtually impossible with a try/catch
approach.
Thrown exceptions are historically difficult to trace. They require implicit knowledge of the implementation details of the code that might throw exceptions. This might go several layers down and leads to tight coupling between modules.
The Lens SDK still throws exceptions where the error is not a "normal execution scenario".
These are considered real "exceptional circumstances" and not alternative outcomes and it's up to the consumer to try/catch
them.
An example of errors that are thrown by the SDK is InvariantError. They are often thrown as result of a misuse of the SDK.
By throwing them we want to fail fast so the consumer can fix the issue as soon as possible.
Specifically for InvariantError
, there is no need to code defensively against these errors. Just rectify the coding issue and move on.
Control flow
const result: Result<number, RangeError> = doSomething();
if (result.isFailure()) {
// because of the `isFailure` check above, TS knows that `result` is a `Failure<RangeError>` here
console.log(result.error); // result.error gets narrowed to `RangeError`
return; // early return
}
// because of the `isFailure` check above and the early return, TS knows that `result` is a `Success<number>` here
console.log(result.value); // result.value gets narrowed to `number`
Exhaustive error handling
Given a result type like the following:
const result: Result<number, PendingSigningError | WalletConnectionError> = doSomething();
You can use a function with a switch
statement to perform exhaustive error handling:
function format(failure: Failure<PendingSigningError | WalletConnectionError>): string {
switch (failure.error.name) {
case 'PendingSigningError':
return 'Please sign the transaction';
break;
case 'WalletConnectionError':
return 'Please connect your wallet and try again';
break;
}
// any code after the switch statement is unreachable
}
The example above assumes allowUnreachableCode: false
in your tsconfig.json
.
An even more robust way to perform exhaustive error handling with a switch
is to use the never
type: see exhaustiveness checking.
A
Result
type represents eitherSuccess
orFailure
.TL;DR
Result
is a minimalist implementation of a value that can be a "success" or a "failure". It borrows from what done in other modern languages (i.e. Rust, Kotlin, Swift, etc.).The Lens SDK adopts this pattern in order to: