Repository: OliverBrotchie/optionals
Branch: main
Commit: f243aa35699e
Files: 12
Total size: 37.6 KB
Directory structure:
gitextract_b_g0ihly/
├── .gitignore
├── .vscode/
│ └── settings.json
├── LICENSE
├── README.md
├── deno.json
├── mod.ts
├── scripts/
│ ├── build_npm.json
│ └── build_npm.ts
└── src/
├── option.ts
├── result.ts
└── tests/
├── option.test.ts
└── result.test.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.DS_Store
coverage
npm
================================================
FILE: .vscode/settings.json
================================================
{
"deno.enable": true,
"deno.lint": true,
"deno.unstable": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2021 Oliver Brotchie
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
Optionals
Rust-like error handling and options for TypeScript, Node and Deno!
This module allows you to remove `null` and `undefined` from your projects with the help of ES6 Symbols and helper methods. Inspired by Rust's `Option`, `Result` enums.
## Why should you use Optionals?
The standard practice of returning `null` or `undefined` when no other value can be returned means that there is no simple way to express the difference between a function that has returned "nothing" and a `null` return type. There are also no easy ways to handle errors in a functional pattern. Rust's implementation of `Option` and `Result` guarantees correctness by expressly forcing correct result-handling practices.
This module provides a minimal, fast and simple way to create expressive functions and perform better pattern matching on resulting values! 🚀
## Usage
```ts
// Result
import { Result, Ok, Err } from "https://deno.land/x/optionals@v2.0.2/mod.ts";
// Option
import {
Option,
Some,
None,
} from "https://deno.land/x/optionals@v2.0.2/mod.ts";
```
## Documentation
Please find further documentation on the [doc](https://doc.deno.land/https://deno.land/x/optionals@v2.0.2/mod.ts) page!
================================================
FILE: deno.json
================================================
{
"tasks": {
"test": "deno test --coverage=coverage",
"lcov": "deno coverage coverage --lcov --output=coverage/report.lcov",
"cover": "deno task clean && deno task test && deno task lcov && genhtml -o coverage/html coverage/report.lcov",
"build": "deno run -A scripts/build_npm.ts --cp=LICENSE,README.md",
"publish": "cd ./npm && npm publish",
"clean": "rm -rf ./npm ./coverage"
}
}
================================================
FILE: mod.ts
================================================
/**
* # `Optionals`
*
* **Rust-like error handling and options for TypeScript and Deno!**
*
* This module provides two classes `Result` and `Option`:
* - `Option` provides a lovely way to express functions that may return nothing.
* - `Result` lets you tackle errors using with an easy to use functional pattern.
*
*/
import { Result, Ok, Err } from "./src/result.ts";
import { Option, Some, None, none } from "./src/option.ts";
export { Result, Ok, Err, Option, Some, None, none };
================================================
FILE: scripts/build_npm.json
================================================
{
"$schema": "https://json.schemastore.org/package.json",
"name": "rust-optionals",
"version": "3.0.0",
"description": "Rust-like error handling and options for TypeScript, Node, and Deno!",
"keywords": [
"rust",
"error-handling",
"option",
"result",
"optionals"
],
"homepage": "https://github.com/OliverBrotchie/optionals#readme",
"bugs": "https://github.com/OliverBrotchie/optionals/issues",
"license": "MIT",
"contributors": [
"Oliver Brotchie "
],
"repository": {
"type": "git",
"url": "https://github.com/OliverBrotchie/optionals.git"
}
}
================================================
FILE: scripts/build_npm.ts
================================================
/*
* Contributed 2022 by Aaron Huggins under the MIT license.
*/
import { parse } from "https://deno.land/std@0.160.0/flags/mod.ts";
import { basename } from "https://deno.land/std@0.160.0/path/mod.ts";
import {
inc as increment,
ReleaseType,
} from "https://deno.land/std@0.160.0/semver/mod.ts";
import {
build,
BuildOptions,
emptyDir,
} from "https://deno.land/x/dnt@0.31.0/mod.ts";
await emptyDir("./npm");
function versionHandler(current: string, releaseType: ReleaseType): string {
const releaseTypes: ReleaseType[] = [
"major",
"minor",
"patch",
"pre",
"premajor",
"preminor",
"prepatch",
"prerelease",
];
if (releaseTypes.includes(releaseType)) {
return increment(current, releaseType) ?? current;
}
return current;
}
const scriptName = basename(import.meta.url, ".ts");
const logTag = `[${scriptName}]`;
const packageFile = `./scripts/${scriptName}.json`;
const packageText = await Deno.readTextFile(packageFile);
const packageJSON = JSON.parse(packageText);
const { version } = packageJSON;
const args = parse(Deno.args, {
string: ["cp", "release"],
default: {
cp: "",
release: "",
},
});
const release = args.release;
packageJSON.version = versionHandler(version, release);
await build({
entryPoints: ["./mod.ts"],
outDir: "./npm",
shims: {
deno: true,
},
package: {
...packageJSON,
},
});
// post build steps
for (const filepath of args.cp.split(/,/g)) {
await Deno.copyFile(filepath, `npm/${filepath}`);
}
if (packageJSON.version === version) {
console.log(
`${logTag} Version did not change; nothing to deploy. ${packageJSON.name} v${version}`
);
} else {
await Deno.writeTextFile(packageFile, JSON.stringify(packageJSON, null, 2));
console.log(
`${logTag} ${packageJSON.name} v${packageJSON.version} ready to deploy!`
);
}
================================================
FILE: src/option.ts
================================================
import { Err, Ok, Result } from "./result.ts";
/**
* The primitive None value.
*
* _Note: To construct a None variant Option, please use `None()` instead._
*/
export const none = Symbol("None");
/**
* A Rust-like Option class.
*
* _Note: Please use either `Some` or `None` to construct an Option._
*
* @example
* ```
* function divide(left: number, right: number): Option {
* if (right === 0) return None();
*
* return Some(left / right);
* }
*
* ```
*/
export class Option {
private val: T | typeof none;
/**
* A constructor for an Option.
*
* _Note: Please use either `Some` or `None` to construct Options._
*
* @param {T | typeof none} input The value to wrap in an Option.
*/
constructor(input: T | typeof none) {
this.val = input;
}
/**
* Converts Option into a String for display purposes.
*/
get [Symbol.toStringTag]() {
return `Option`;
}
/**
* Iterator support for Option.
*
* _Note: This method will only yeild if the Option is Some._
* @returns {IterableIterator}
*/
*[Symbol.iterator]() {
if (this.isSome()) yield this.val;
}
/**
* Returns true if contained value isnt None.
* @returns {boolean}
*/
isSome(): boolean {
return this.val !== none;
}
/**
* Returns true if contained value is None.
*
* @returns {boolean}
*/
isNone(): boolean {
return this.val === none;
}
/**
* Returns the contained Some value, consuming the Option.
* Throws an Error with a given message if the contained value is None.
*
* @param {string} msg An error message to throw if contained value is None.
* @returns {T}
*/
expect(msg: string): T {
if (this.isNone()) {
throw new Error(msg);
}
return this.val as T;
}
/**
* Returns the contained Some value, consuming the Option.
* Throws an Error if contained value is None.
*
* @returns {T}
*/
unwrap(): T {
if (this.isNone()) {
throw new Error(`Unwrap called on None`);
}
return this.val as T;
}
/**
* Returns the contained Some value or a provided default.
*
* @param {T} fallback A default value to return if contained value is an Option.
* @returns {T}
*/
unwrapOr(fallback: T): T {
if (this.isNone()) {
return fallback;
}
return this.val as T;
}
/**
* Returns the contained Some value or computes it from a closure.
*
* @param {Function} fn A function that computes a new value.
* @returns {T}
*/
unwrapOrElse(fn: () => T): T {
if (this.isNone()) {
return fn();
}
return this.val as T;
}
/**
* Maps an Option to Option by applying a function to a contained Some value, leaving None values untouched.
*
* @param {Function} fn A mapping function.
* @returns {Option}
*/
map(fn: (input: T) => U): Option {
if (this.isSome()) {
return new Option(fn(this.val as T));
}
return this as unknown as Option;
}
/**
* Returns the provided fallback (if None), or applies a function to the contained value.
*
* @param {U} fallback A defualt value
* @param {Function} fn A mapping function.
* @returns {U}
*/
mapOr(fallback: U, fn: (input: T) => U): U {
if (this.isSome()) {
return fn(this.val as T);
}
return fallback;
}
/**
* Returns `or` if the Option is None, otherwise returns self.
*
* @param {Option} or An alternative Option value
* @returns {Option}
*/
or(or: Option): Option {
if (this.isSome()) {
return this;
}
return or;
}
/**
* Transforms the `Option` into a `Result`, mapping Some to Ok and None to Err.
*
* @param {E} err An error to return if the Option is None.
* @returns {Result}
*
* @example
* ```
* const result = Some(2).okOr("Error"); // => Ok(2)
* ```
*/
okOr(err: E | string): Result {
if (this.isSome()) {
return Ok(this.val as T);
} else {
return Err(err);
}
}
/**
* Returns contained value for use in matching.
*
* _Note: Please only use this to match against in `if` or `swtich` statments._
*
* @returns {T | typeof none}
* @example
* ```ts
* function coolOrNice(input: Option): Option {
* switch (input.peek()) {
* case "cool":
* console.log("Input was the coolest!");
* break;
* case "nice":
* console.log("Input was was the nicest!");
* break
* default:
* return None();
* }
* return Some()
* }
* ```
*/
peek(): T | typeof none {
return this.val;
}
/**
* Converts from Option