Skip to main content

@w5s/core

W5S Core modules (@w5s/core)

NPM Version License

Installation

npm install @w5s/core

Usage

Enforce STD to write better code

VanillaJSSTDExplanation
null, undefinedOptionStop spending time choosing between undefined and null. Based on Microsoft standard, undefined (== Option.None) is preferred.
throw new Error()return Result.Error(new Error())Error throwing / Promise rejection is a mechanism that should only be used to stop the execution of a program. When a computation represents an expected failure (ex: parsing, data fetching), Result should be used.
PromiseTaskTask is like a Promise but lazily evaluated. It has numerous advantages (composable, etc). See Article
N/ATime, DurationTagged types that makes the unit of time explicit (milliseconds). Some libraries could use seconds or minutes implicitly which is confusing
setTimeout(fn, ms)Task.andThen(Time.delay(ms), fn)setTimeout is impure, create a task that will run after Time.delay.
Date.nowTime.nowDate.now is impure, use Time.now that is a Task.
console.debugConsole.debugconsole.debug is impure, use Console.debug that is a Task.
Math.randomrandomNumberMath.random is impure, use randomNumber that is a Task.
UUID, ...TaskMore impure function, wrap them in a Task()
N/AIntA tagged type that narrows number to only the safe integer values
[].map, [].filter, ...Array.map, Array.filter, ...Array module contains all immutable operations on arrays.

Example

import { Result } from '@w5s/core';

function parseNumber(expr: string) {
const parsed = Number(expr);

// - Return a immutable Result object
// - Avoid throwing error because impure
// - Avoid using NaN because the error case is implicit in the typing
return Number.isNaN(parsed) ? Result.Ok(parsed) : Result.Error('NotANumber');
}

export function main() {
const parsed = parseNumber('1.1'); // Result.Ok(1.1)
return Result.map(parsed, (amount) => amount + 2); // Result.Ok(3.1)
}

// runTask is impure and should be put at the edge of the program
void main(); // prints { _: 'Result/Ok', value: 3.1 }

License

MIT © Julien Polo julien.polo@gmail.com

Index

Other

Array

Array<Item>: ReadonlyArray<Item>

Type parameters

  • Item

CallableFunction

CallableFunction<T>: T[typeof Callable.symbol] & T

A callable function with callable interface


Type parameters

Char

Char: CharType

Character value

Alias of @w5s/core!Type.Char

Enum

Enum<T>: T & Enumerable<Omit<T, typeName>>

Type parameters

  • T: Record<string, any> = Record<string, unknown>

InspectFunction

InspectFunction: (anyValue: unknown, options: InspectOptions) => string

InspectOptions

InspectOptions: Record<string, unknown>

Int

Int: IntType

Integer value

Alias of @w5s/core!Type.Int

JSONValue

JSONValue: null | boolean | number | string | ReadonlyArray<JSONValue> | {}

Any valid JSON value

Option

Option<Value>: Value | Option.None

Option<Value> is the type used to represent either a defined value Some<Value> or None (i.e. null or undefined)

This module focuses on handling null and undefined values in a functional style, to avoid throwing errors at runtime. Because Option.None = undefined, this modules provides an opinionated way to remove confusion between null and undefined.

Some other libraries uses object to implement Maybe/Option monad but has drawbacks :

  • ECMAScript already uses nullable/undefined values everywhere
  • Each library that does not uses the Some/None must be patched / overridden
  • it creates a third empty value None in addition to null and undefined
@example
const getName = (num) => num % 2 === 0? Option.Some('Django') : Option.None;
const displayName = (option) => Option.map(option, (value) => 'name: '+ value);
const print = (option) => {
if (Option.isNone(option)) {
console.warn('None');
} else {
console.log('Some(', option, ')');
}
}

for (let i = 0; i < 6; i++) {
const option = displayName(getName(i));
print(option); // alternate console.log('Some(Django)'); and console.warn('None');
}

Type parameters

  • Value

    the type of the contained value

Record

Record<Key, Value>: { readonly [ P in Key ]: Value }

A Record is an immutable mapping { [string]: value }


Type parameters

RecordKey

RecordKey: string | symbol

Result

Result<Value, Error>: Result.Ok<Value> | Result.Error<Error>

Result<Value, Error> is the type used to represent either a Result.Result.Ok<Value> or a Result.Error<Error> as a function return value. Result.Result.Ok<Value> should be used when the result is the expected value. Result.Error<Error> should be used when recoverable error occurred (i.e. an error that does not require the program to stop)

Results can be used with async functions returning Promise<Result<Value, Error>>.

Important: Rejected Promise and thrown Error should only occur when the program has an unexpected state that should stop the execution

@example
const getName = (num) => num % 2 === 0? Result.Ok('Django') : Result.Error('error!');
const displayName = (result) => Result.map(result, (value) => 'name: '+ value);
const print = (result) => {
if (Result.isError(result)) {
console.error(result.error);
} else {
console.log(result.value);
}
}

for (let i = 0; i < 6; i++) {
const result = displayName(getName(i));
print(result); // alternate console.log('name: Django'); and console.error('error!');
}

Type parameters

  • Value

    the type of value in case of Ok

  • Error

    the type of error in case of Error

Struct

Struct<Properties>: Readonly<Properties>

An Immutable Data Object with a type identifier

@example
// Interface have a better appearance in VSCode
export interface MyType extends Struct<{
[Struct.type]: 'MyType',
foo: boolean;
}> {}

Type parameters

  • Properties: { _: string }

Tag

Tag<T>: CoreTypeTag<T>

Alias of @w5s/core-type!Tag


Type parameters

  • T: string | symbol