twitten
Version:
A fluent js/ts implementation of the result monad, utilizing Happy/Sad path terminology.
94 lines (66 loc) • 4.06 kB
Markdown
## Twitten - type safe & expressive error-handling
[](https://travis-ci.org/Kusken/Twitten)
[](https://kusken.github.io/Twitten/modules/_index_.html)
[](https://github.com/Kusken/Twitten/blob/master/LICENSE)
[](https://nodei.co/npm/twitten/)
## Problems solved
- Type-safe error-handling for JS & TS
- Ubiquitous language for error-handling
## Introduction
The twitten library lets you handle errors in JavaScript and TypeScript in a **type safe** and **expressive** way.
The way this is done is nothing new, especially if you're familiar with how success and error cases are handled in the functional world. What's unique as far as I know is that this little library utlize _'Happy & Sad path'_ temrinology drawn from the world of testing.
## Examples
### How we usually do when we only have the try/catch + throw jump statement
```typescript
function createUser(uname: string, pw: string): User {
if (!(uname && pw))
throw new Error("Validation failed!");
return { username: uname, password: pw };
}
try {
const user = createUser("John Doe", "hello123");
console.log(user.username, user.password);
} catch (error) {
throw error;
}
```
#### CONS
- This approach makes our code harder to reason about than it has to be. Since js and ts does not allow us to embedd the _throw_ **jump statement** in our function and method signatures, we have to rip up the hood and read the **implementation details** for clues to what errors we could handle.
- There's more to it than that! Like other jump statements the **throw** statement makes the control flow of our applications harder to reason about. The control flow following an expected path from **module** `a -> b -> c...` is disturbed. We could have the control go from **module** `a -> d...` or `a -> b -> c -> g -> p -> x..-`. There are many possibilites, and its **hard to reason about!**
- Adding to the stack of _hard to_ reasons, js and ts doesn't have the **powerful catch** clause that we know from languages like C#.
### The Altenrative - `import { Path, Happy, Sad } from "twitten"`
#### Path with explicit type checking & unwrap
```typescript
function createUser(uname: string, pw: string): Path<User> {
if (!(uname && pw))
return Sad.path(new Error("Something went wrong!"));
return Happy.path({ username: uname, password: pw });
}
//Must do explicit type checking before we know the correct type of outcome
const user = createUser("John Doe", "hello123");
if (Path.isHappy(user.outcome))
console.log(user.outcome.username, user.outcome.password);
else //outcome.message refers to 'Error.message'
console.log(user.outcome.message);
```
#### Path with continuations
```typescript
const user = createUser("John Doe", "hello123");
user.onHappyPath(usr => console.log(usr.username, usr.password))
.onSadPath(error => console.log(error.message));
```
#### Path with more continuations
```typescript
const user = createUser("John Doe", "hello123");
user.onHappyPath(usr => {
console.log(usr.username, usr.password);
return usr;
}).onHappyPath(usr => usr.username.replace(" ", "_"))
.onHappyPath(uname => console.log(uname));
```
#### Conclusion
By embedding errors within the type system, we force awarness of potential failures upon consumers of our code. By doing so we make a clear distinction between _**unexpected**_ and _**exceptional**_ errors (exceptions) on one side, and _**expected**_ errors (sad paths) on the other.
If an exception bubbles up to the end-user it will not have anything to with the expected like a input validation error, so we know that something went seriously wrong.
## For documentation see
- [Tests](https://github.com/Kusken/Twitten/blob/master/tests/Path.spec.ts)
- [Docs](https://kusken.github.io/Twitten/index.html)