JSDoc Example: Result
JSDoc Example: Result
JSDoc Example: Result
As mentioned, I don't use TypeScript, but write
many of my apps in pure Javascript.
As such, in order to describe types,
I use JSDoc extensively in many of my codebases.
Hoping someone would find it interesting,
I have a several pages presenting
JSDoc examples as to how you could effectively
utilize JSDoc's classical doctrine syntax.
Here is one of the modules that I currently use
on my website, specifically,
the Result module.
I was inspired by Rust's Result<T, E> type.
/**
* `Result` is a monad representing either
* a success ('Ok') or a failure ('Err').<br />
* Inspired by Rust's `Result<T, E>` type.
* ```
* const { Result } = await import('./Result.js');
* const r1 = Result().ok(42);
* console.log(r1.unwrap()); // 42
*
* const r2 = Result().err(new Error('fail'));
* console.log(r2.unwrap_or('default')); // "default"
*
* // map / flatMap
* const r3 = Result(2).map(function (x) { return x * 3; }); // Ok(6)
* const r4 = Result(2).flatMap(function (x) { return Result(x + 5); }); // Ok(7)
*
* // match pattern
* r3.match({
* Ok: function (v) { console.log('Ok:', v); },
* Err: function (e) { console.error('Err:', e); }
* });
* ```
* There are more fancy methods available:
* ```
* try {
* result.unwrap_or_throw();
* } catch(err) {
* console.log(err);
* }
* ```
*
* This module also has definitions for `ResultEntity` and `ResultInterface`.<br />
* However, while they are both defined in the same file, JSDoc generates<br />
* separate pages for `ResultEntity` and `ResultInterface`.
* @module lib/Result
* @see {@link module:lib/Result~ResultInterface|ResultInterface} - Although defined in the same file, JSDoc generates a separate page
*/
/**
* __ResultEntity (instance created)__<br />
* `ResultEntity` is a monad holding `Ok` value or `Err`.<br />
* To create an instance, run `Result` factory.
* @typedef ResultEntity
* @implements module:lib/Result~ResultInterface<T,E>
* @template T, E
* @property {function(T):ResultEntity<T,E>} ok - Marks this Result as Ok.
* @property {function(E):ResultEntity<T,E>} err - Marks this Result as Err.
* @property {function():boolean} is_ok - Returns true if Ok.
* @property {function():boolean} is_err - Returns true if Err.
* @property {function():T} unwrap - Returns Ok or throws if Err.
* @property {function(*):*} unwrap_or - Returns Ok or fallback.
* @property {function():E} unwrap_err - Returns the Err value.
* @property {function():void} throw_if_err - Throws if Err.
* @property {function():T} unwrap_or_throw - Throws if Err, else returns Ok.
* @property {ResultMapper<T,U,E>} map - Maps Ok through a function.
* @property {ResultMapper<T,U,E>} flatMap - Maps and flattens nested Result.
* @property {ResultMatcher<T,E>} match - Pattern matches on Ok/Err.
* @see {@link module:lib/Result~ResultInterface|ResultInterface} - For the interface of "ResultEntity"
*/
/**
* A mapper function that transforms the `Ok` value.
* @callback ResultMapper
* @template T,U,E
* @param {T} value
* @returns {ResultEntity<U,E>}
*/
/**
* A function for pattern matching on `Result` values.
* @callback ResultMatcher
* @template T,E
* @param {{Ok:function(T):any, Err:function(E):any}} pattern
* @returns {any}
*/
/**
* __ResultInterface (interface)__<br />
* Interface design for `ResultEntity`.
* @interface ResultInterface
* @template T, E
* @see {@link module:lib/Result~ResultEntity|ResultEntity} - Although defined in the same file, generates 2 separate pages.
*/
/**
* __Result (factory)__<br />
* A factory function to create `ResultEntity`.<br />
* @function
* @name Result
* @template T, E
* @param {T} [value=] - If given optionally, it will set the given value as `Ok` value, and will set the monad `Ok`.
* @returns {ResultEntity<T,E>}
* @see {@link module:lib/Result~ResultEntity|ResultEntity} - See definitions of the returned "ResultEntity".
* @see {@link module:lib/Result~ResultInterface|ResultInterface} - For the interface of "ResultEntity".
*/
export const Result = value => {
/**
* @type {ResultEntity<T,E>}
*/
const self = {};
/**
* @type {boolean}
*/
let _ok = false;
/**
* @type {?E}
*/
let _err = null;
/**
* @type {?T}
*/
let _value = null;
__init(value);
/**
* For the given value, sets it as `Ok` value, and sets the monad `Ok`.
* @function
* @method module:lib/Result~ResultInterface#ok
* @param {T} value
* @returns {ResultEntity<T,E>}
*/
self.ok = value => {
_ok = true;
_err = null;
_value = value;
return Object.freeze(self);
};
/**
* For the given value, sets it as `Err` value, and sets the monad `Err`.
* @function
* @method module:lib/Result~ResultInterface#err
* @param {E} err
* @returns {ResultEntity<T,E>}
*/
self.err = err => {
_ok = false;
_err = err;
_value = null;
return Object.freeze(self);
};
/**
* Checks if the monad is `Err`.
* @function
* @method module:lib/Result~ResultInterface#is_err
* @returns {boolean}
*/
self.is_err = () => !_ok;
/**
* Checks if the monad is `Ok`.
* @function
* @method module:lib/Result~ResultInterface#is_ok
* @returns {boolean}
*/
self.is_ok = () => _ok;
/**
* Returns the `Ok` value or throws if `Err`.
* @function
* @method module:lib/Result~ResultInterface#unwrap
* @throws {Error}
* @returns {T}
*/
self.unwrap = () => {
if (!_ok) {
const message =
typeof _err?.message === 'string'
? _err.message
: '';
throw new Error(
`unwrap() called on Err: ${message}`
);
}
return _value;
};
/**
* Returns `Ok` or a fallback value.
* @function
* @method module:lib/Result~ResultInterface#unwrap_or
* @template U
* @param {U} given
* @returns {T | U}
*/
self.unwrap_or = given => (!!_ok ? _value : given);
/**
* Regardless of whether the monad being `Ok` or `Err`, returns the internally held `Err`.
* @function
* @method module:lib/Result~ResultInterface#unwrap_err
* @returns {E}
*/
self.unwrap_err = () => _err;
/**
* Throws the `Err` if present.
* @function
* @method module:lib/Result~ResultInterface#throw_if_err
* @throws {E}
*/
self.throw_if_err = () => {
if (!_ok) throw _err;
};
/**
* Returns `Ok` or throws the `Err`.
* @function
* @method module:lib/Result~ResultInterface#unwrap_or_throw
* @throws {*}
* @returns {T}
*/
self.unwrap_or_throw = () => {
self.throw_if_err();
return self.unwrap();
};
/**
* Maps the `Ok` value through a function `function(T):U`.
* @method module:lib/Result~ResultInterface#map
* @template U
* @function
* @param {function(T):U} fn - Mapping function `function(T):U` for transforming `Ok` values.
* @returns {ResultEntity<U,E>}
* @see {@link module:lib/Result~ResultMapper|ResultMapper}
*/
self.map = function (fn) {
if (_ok) {
try {
return Result(fn(_value));
} catch (err) {
return Result().err(/** @type {E} */ (err));
}
}
return Result().err(_err);
};
/**
* Maps and flattens nested `Result`s `function(T):ResultEntity<U,E>`.
* @method module:lib/Result~ResultInterface#flatMap
* @template U
* @function
* @param {function(T):ResultEntity<U,E>} fn - Mapping function `function(T):ResultEntity<U,E>` returning another `Result`.
* @returns {ResultEntity<U,E>}
* @see {@link module:lib/Result~ResultMapper|ResultMapper}
*/
self.flatMap = function (fn) {
if (_ok) {
try {
const next = fn(_value);
if (
next &&
typeof next.is_ok === 'function'
) {
return next;
}
return Result().err(
/** @type {E} */ (
new Error(
'flatMap did not return a Result'
)
)
);
} catch (err) {
return Result().err(/** @type {E} */ (err));
}
}
return Result().err(_err);
};
/**
* Pattern matches on `Ok` or `Err` values<br />
* `function({Ok:function(T):any, Err:function(E):any}):any`.
* @method module:lib/Result~ResultInterface#match
* @param {{Ok:function(T):any, Err:function(E):any}} pattern - Pattern matcher `function({Ok:function(T):any, Err:function(E):any}):any`.
* @see {@link module:lib/Result~ResultMatcher|ResultMatcher}
* @returns {any}
*/
self.match = function (pattern) {
if (_ok) {
return pattern.Ok(_value);
}
return pattern.Err(_err);
};
function __init(value) {
if (typeof value !== 'undefined') {
self.ok(value);
}
}
return Object.freeze(self);
};
Your privacy
By clicking "OK" you agree Astralscience can store cookies on your device and disclose information in accordance with our Cookie Policy.
個人情報の保護について
以下 OK をクリックすることにより、Astralscience があなたの端末にクッキーを保存し、クッキー・ポリシー に従って情報を開示することに同意します。
Quyền riêng tư của bạn
Bằng cách nhấn "OK", bạn đồng ý rằng Astralscience có thể lưu cookie trên thiết bị của bạn và xử lý thông tin theo Chính sách Cookie của chúng tôi.
