Last active
June 27, 2021 13:49
-
-
Save moatorres/deb3bf797c3bc5ca63c5a7919508b295 to your computer and use it in GitHub Desktop.
Maybe monad in JS/Node (without classes)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { Maybe } = require('./Maybe') | |
// let's say we have a function that could return null or undefined | |
function divide(a, b) { | |
if (b === 0) return | |
return (a / b) | |
} | |
let resultsSomething = divide(4, 1) | |
let resultsNothing = divide(4, 0) | |
let mayJust = Maybe.fromNullable(divide(4, 1)) | |
console.log('type:', mayJust.type) // => Symbol(:just) | |
console.log('has:', mayJust.has()) // => true | |
console.log('isJust:', mayJust.isJust()) // => true | |
console.log('isNothing:', mayJust.isNothing()) // => false | |
console.log('unwrap:', mayJust.unwrap()) // => 4 | |
console.log('map:', mayJust.map((v) => v * 1303).getOrExecute((v) => 'what?')) // => 5212 | |
console.log('flatMap:', mayJust.flatMap((v) => v + 100)) // => 104 | |
console.log('filter:', mayJust.filter((v) => typeof v !== 'number').unwrap()) // → Nothing() | |
console.log('toString:', mayJust.toString()) // => 'Just(1)' | |
console.log('toArray:', mayJust.toArray()) // => [ 4 ] | |
console.log('or:', mayJust.or('Else')) // => 4 | |
console.log('getOrElse:', mayJust.getOrElse('Else')) // => 4 | |
console.log('getOrExecute:', mayJust.getOrExecute(() => 'Nada')) // => 4 | |
console.log('getOrThrow:', mayJust.getOrThrow(new Error('Ops!'))) // => 4 | |
let tapper = { nothing: () => 'Nada', just: (v) => v } | |
console.log('tap:', mayJust.tap(tapper)) // => 4 | |
console.log('match:', mayJust.map((v) => (v = undefined)).match(tapper)) // => Nada | |
// .of and .fromValue are aliases of .fromNullable | |
let mayNothing = Maybe.of(divide(4, 0)) | |
console.log('type:', mayNothing.type) // => Symbol(:nothing) | |
console.log('has:', mayNothing.has()) // => false | |
console.log('isJust:', mayNothing.isJust()) // => false | |
console.log('isNothing:', mayNothing.isNothing()) // => true | |
console.log('unwrap:', mayNothing.unwrap()) // → Nothing { ... } | |
console.log('map:', mayNothing.map((v) => v + 100).unwrap()) // → Nothing { ... } | |
console.log('flatMap:', mayNothing.flatMap((v) => v + 100)) // → Nothing { ... } | |
console.log('filter:', mayNothing.filter((v) => typeof v !== 'number').unwrap()) // → Nothing { ... } | |
console.log('toString:', mayNothing.toString()) // 'Nothing()' | |
console.log('toArray:', mayNothing.toArray()) // => [] | |
console.log('or:', mayNothing.or('Else indeed')) // => 'Else indeed' | |
console.log('getOrElse:', mayNothing.getOrElse('Else')) // => 'Else' | |
console.log('getOrExecute:', mayNothing.getOrExecute(() => 'Nada')) // => 'Nada' | |
console.log('getOrThrow:', mayNothing.getOrThrow(new Error('Ops!'))) // => Error: Ops! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type NotNullOrUndefined = string | number | boolean | symbol | object | |
interface IMaybe<T> { | |
type: symbol | |
has(): boolean | |
isJust(): boolean | |
isNothing(): boolean | |
unwrap(): T | never | |
map<U>(fn: (value: T) => U): IMaybe<U> | |
flatMap<U>(fn: (value: T) => U): T | |
filter<U>(fn: (value: T) => U): IMaybe<U> | |
andThen<U>(fn: (value: T) => IMaybe<U>): T | |
and<U>(option: IMaybe<U>): IMaybe<U> | T | |
toString(): string | |
toArray(): any[] | |
valueOrUndefined(): T | undefined | |
valueOrNull(): T | null | |
or<U>(option: IMaybe<U>): IMaybe<T | U> | |
getOrElse<U>(option: IMaybe<U> | NotNullOrUndefined): IMaybe<T | U> | |
getOrThrow<U>(option: IMaybe<U> | Error): IMaybe<T | U> | Error | |
getOrExecute<U>( | |
fn: Function | IMaybe<U> | |
): IMaybe<T | U> | NotNullOrUndefined | Error | |
tap<U>(fn: IMatch<T, U>): U | |
match<U>(fn: IMatch<T, U>): U | |
} | |
interface IJust<T> extends IMaybe<T> { | |
unwrap(): T | |
valueOrUndefined(): T | |
valueOrNull(): T | |
map<U>(fn: (value: T) => U): IJust<U> | |
or<U>(opt: IMaybe<U>): IMaybe<T> | |
and<U>(opt: IMaybe<U>): IMaybe<U> | |
} | |
interface INothing<T> extends IMaybe<T> { | |
unwrap(): never | |
valueOrUndefined(): undefined | |
valueOrNull(): null | |
map<U>(fn: (value?: T) => U): INothing<U> | |
or<U>(opt: IMaybe<U> | U): IMaybe<U> | U | |
and<U>(opt: IMaybe<U>): INothing<U> | |
} | |
interface IMatch<T, U> { | |
just: (value: T | (() => U)) => U | |
nothing: (() => U) | U | |
} | |
interface IMaybeRef<T> { | |
just<T>(value?: any): IMaybe<T | U> | |
nothing<U>(value?: any): INothing<T> | |
fromNullable<U>(value?: any): IMaybe<T | U> | |
fromValue<U>(value?: any): IMaybe<T | U> | |
of<U>(value?: any): IMaybe<T | U> | |
} | |
export const MaybeType = { | |
Just: symbol, | |
Nothing: symbol, | |
} | |
export const Maybe: IMaybeRef<T> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { isNumberZero, isFalse, isEmptyString, notNullOrUndefined, isValid, makeReadOnly } = require('./utils') | |
const MaybeType = { | |
Just: Symbol(':just'), | |
Nothing: Symbol(':nothing'), | |
} | |
const makeJust = (value) => makeReadOnly({ | |
type: MaybeType.Just, | |
has: () => true, | |
isJust: () => true, | |
isNothing: () => false, | |
unwrap: () => value, | |
map: (f) => Maybe.of(f(value)), | |
flatMap: (f) => f(value), | |
filter: (f) => Maybe.fromNullable(f(value) ? value : null), | |
andThen: (fn) => fn(value), | |
and: (_) => _, | |
toString: () => `Just(${value})`, | |
toArray: () => (Array.isArray(value) ? value : [value]), | |
valueOrUndefined: () => value, | |
valueOrNull: () => value, | |
getOrElse: (_) => value, | |
getOrThrow: (_) => value, | |
getOrExecute: (_) => value, | |
or(_) { | |
return this | |
}, | |
tap: (obj) => { | |
const { just } = obj | |
if (typeof just === 'function') { | |
return just() | |
} | |
return just | |
}, | |
match: function (obj) { | |
return this.tap(obj) | |
}, | |
}) | |
const makeNothing = (value) => makeReadOnly({ | |
type: MaybeType.Nothing, | |
has: () => false, | |
isNothing: () => true, | |
isJust: () => false, | |
unwrap: () => Maybe.nothing(), | |
map: (_) => Maybe.nothing(), | |
flatMap: () => Maybe.nothing(), | |
filter: () => Maybe.nothing(), | |
andThen: (_) => Maybe.nothing(), | |
and: (_) => Maybe.nothing(), | |
toString: () => 'Nothing()', | |
toArray: () => [], | |
valueOrUndefined: () => undefined, | |
valueOrNull: () => null, | |
or: (option) => option, | |
getOrExecute: (fn) => { | |
if (!fn) | |
throw new Error('Cannot call getOrExecute with a missing function.') | |
return fn() | |
}, | |
getOrElse: (def) => { | |
if (!notNullOrUndefined(def)) | |
throw new Error('Cannot call getOrElse with a missing value.') | |
return def | |
}, | |
getOrThrow(error) { | |
if (notNullOrUndefined(error)) throw error | |
else throw new Error() | |
}, | |
tap: (obj) => { | |
const { nothing } = obj | |
if (typeof nothing === 'function') { | |
return nothing() | |
} | |
return nothing | |
}, | |
match: function (obj) { | |
return this.tap(obj) | |
}, | |
}) | |
const Maybe = makeReadOnly({ | |
just: (value) => { | |
if (isValid(value)) return makeJust(value) | |
throw new Error('Just cannot have an null or undefined value') | |
}, | |
nothing: () => makeNothing(), | |
fromNullable: function (value) { | |
return isValid(value) ? this.just(value) : this.nothing() | |
}, | |
fromValue: function (value) { | |
return this.fromNullable(value) | |
}, | |
of: function (value) { | |
return this.fromNullable(value) | |
}, | |
}) | |
module.exports = { Maybe, MaybeType } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { MaybeType, Maybe } = require('./Maybe') | |
let nops, yep, tapMatch | |
beforeEach(() => { | |
nops = Maybe.fromNullable(null) | |
yep = Maybe.fromNullable(1) | |
tapMatch = { | |
just: () => 'Hey!', | |
nothing: () => 'Putz!', | |
} | |
}) | |
describe('Maybe', () => { | |
it('Should be defined', () => { | |
expect(Maybe).toBeDefined() | |
}) | |
describe('.just()', () => { | |
it('Should be defined', () => { | |
expect(Maybe.just).toBeDefined() | |
}) | |
it('Should return a Just type object if the value is not null or undefined', () => { | |
let justa = Maybe.just(1) | |
expect(justa.type).toBe(MaybeType.Just) | |
}) | |
it('Should throw if the value is null or undefined', () => { | |
expect(() => Maybe.just()).toThrowError() | |
}) | |
}) | |
describe('.nothing()', () => { | |
it('Should be defined', () => { | |
expect(Maybe.nothing).toBeDefined() | |
}) | |
it('Should return a Nothing type object regardless of the value', () => { | |
let not = Maybe.nothing(1) | |
let notReally = Maybe.nothing(0) | |
expect(not.type).toBe(MaybeType.Nothing) | |
expect(notReally.type).toBe(MaybeType.Nothing) | |
}) | |
}) | |
describe('.fromNullable()', () => { | |
it('Should be defined', () => { | |
expect(Maybe.fromNullable).toBeDefined() | |
}) | |
it('Should return a Just type object if the value is not null or undefined', () => { | |
let justa = Maybe.fromNullable(1) | |
expect(justa.type).toBe(MaybeType.Just) | |
}) | |
it('Should return a Nothung type object the value is null or undefined', () => { | |
let not = Maybe.fromNullable() | |
expect(not.type).toBe(MaybeType.Nothing) | |
}) | |
}) | |
describe('.fromValue() → alias of .fromNullable()', () => { | |
it('Should be defined', () => { | |
expect(Maybe.fromValue).toBeDefined() | |
}) | |
it('Should return a Just type object if the value is not null or undefined', () => { | |
let justa = Maybe.fromValue(1) | |
expect(justa.type).toBe(MaybeType.Just) | |
}) | |
it('Should return a Nothung type object the value is null or undefined', () => { | |
let not = Maybe.fromValue() | |
expect(not.type).toBe(MaybeType.Nothing) | |
}) | |
}) | |
describe('.of() → alias of .fromNullable()', () => { | |
it('Should be defined', () => { | |
expect(Maybe.of).toBeDefined() | |
}) | |
it('Should return a Just type object if the value is not null or undefined', () => { | |
let justa = Maybe.of(1) | |
expect(justa.type).toBe(MaybeType.Just) | |
}) | |
it('Should return a Nothung type object the value is null or undefined', () => { | |
let not = Maybe.of() | |
expect(not.type).toBe(MaybeType.Nothing) | |
}) | |
}) | |
}) | |
describe('Just', () => { | |
describe('.type', () => { | |
it('Should be defined', () => { | |
expect(yep.type).toBeDefined() | |
}) | |
it('Should be of Just type', () => { | |
expect(yep.type).toBe(MaybeType.Just) | |
}) | |
}) | |
describe('.has()', () => { | |
it('Should be defined', () => { | |
expect(yep.has).toBeDefined() | |
}) | |
it('Should return "true"', () => { | |
expect(yep.has()).toBeTrue() | |
}) | |
}) | |
describe('.isJust()', () => { | |
it('Should be defined', () => { | |
expect(yep.isJust).toBeDefined() | |
}) | |
it('Should return "true"', () => { | |
expect(yep.isJust()).toBeTrue() | |
}) | |
}) | |
describe('.isNothing()', () => { | |
it('Should be defined', () => { | |
expect(yep.isNothing).toBeDefined() | |
}) | |
it('Should return "false"', () => { | |
expect(yep.isNothing()).toBeFalse() | |
}) | |
}) | |
describe('.unwrap()', () => { | |
it('Should be defined', () => { | |
expect(yep.unwrap).toBeDefined() | |
}) | |
it('Should return an unwrapped value', () => { | |
expect(yep.unwrap()).toEqual(1) | |
}) | |
}) | |
describe('.map()', () => { | |
it('Should be defined', () => { | |
expect(yep.map).toBeDefined() | |
}) | |
it('Should map over the value and return a Just or a Nothing type object', () => { | |
let res1 = yep.map((v) => v + 1) | |
expect(res1.type).toBe(MaybeType.Just) | |
expect(res1.unwrap()).toBe(2) | |
let res2 = yep.map((v) => (v = undefined)) | |
expect(res2.type).toBe(MaybeType.Nothing) | |
}) | |
}) | |
describe('.flatMap()', () => { | |
it('Should be defined', () => { | |
expect(yep.flatMap).toBeDefined() | |
}) | |
it('Should map over the value and return the mapped function result', () => { | |
let res = yep.flatMap((v) => v + 1) | |
expect(res).toEqual(2) | |
}) | |
}) | |
describe('.filter()', () => { | |
it('Should be defined', () => { | |
expect(yep.filter).toBeDefined() | |
}) | |
it('Should return "this" if the result of the predicate function is truthy', () => { | |
let res = yep.filter((v) => typeof v === 'number') | |
expect(res.type).toBe(MaybeType.Just) | |
expect(res.unwrap()).toBe(1) | |
}) | |
it('Should return a Nothing type object if the predicate function result is falsy', () => { | |
let res = yep.filter((v) => typeof v !== 'number') | |
expect(res.type).toBe(MaybeType.Nothing) | |
}) | |
}) | |
describe('.andThen()', () => { | |
it('Should be defined', () => { | |
expect(yep.andThen).toBeDefined() | |
}) | |
it('Should execute the provided function and return its result', () => { | |
let res = yep.andThen(() => 'A-ha!') | |
expect(res).toEqual('A-ha!') | |
}) | |
}) | |
describe('.and()', () => { | |
it('Should be defined', () => { | |
expect(yep.and).toBeDefined() | |
}) | |
it('Should return the provided and argument', () => { | |
let res = yep.and(10) | |
expect(res).toEqual(10) | |
}) | |
}) | |
describe('.toString()', () => { | |
it('Should be defined', () => { | |
expect(yep.toString).toBeDefined() | |
}) | |
it('Should return a string representation of "this"', () => { | |
let res = yep.toString() | |
expect(typeof res).toBe('string') | |
expect(res).toEqual('Just(1)') | |
}) | |
}) | |
describe('.toArray()', () => { | |
it('Should be defined', () => { | |
expect(yep.toArray).toBeDefined() | |
}) | |
it('Should return an array with the enclosed value', () => { | |
expect(yep.toArray()).toEqual([1]) | |
}) | |
}) | |
describe('.valueOrUndefined()', () => { | |
it('Should be defined', () => { | |
expect(yep.valueOrUndefined).toBeDefined() | |
}) | |
it('Should return an unwrapped value', () => { | |
expect(yep.valueOrUndefined()).toEqual(1) | |
}) | |
}) | |
describe('.valueOrNull()', () => { | |
it('Should be defined', () => { | |
expect(yep.valueOrNull).toBeDefined() | |
}) | |
it('Should return an unwrapped value', () => { | |
expect(yep.valueOrNull()).toEqual(1) | |
}) | |
}) | |
describe('.or()', () => { | |
it('Should be defined', () => { | |
expect(yep.or).toBeDefined() | |
}) | |
it('Should return "this"', () => { | |
let res = yep.or() | |
expect(res.type).toBe(MaybeType.Just) | |
expect(res.unwrap()).toEqual(1) | |
}) | |
}) | |
describe('.getOrElse()', () => { | |
it('Should be defined', () => { | |
expect(yep.getOrElse).toBeDefined() | |
}) | |
it('Should return the unwrapped value', () => { | |
expect(yep.getOrElse()).toEqual(1) | |
}) | |
}) | |
describe('.getOrThrow()', () => { | |
it('Should be defined', () => { | |
expect(yep.getOrThrow).toBeDefined() | |
}) | |
it('Should return the unwrapped value', () => { | |
expect(yep.getOrThrow()).toEqual(1) | |
}) | |
}) | |
describe('.getOrExecute()', () => { | |
it('Should be defined', () => { | |
expect(yep.getOrExecute).toBeDefined() | |
}) | |
it('Should return the unwrapped value', () => { | |
expect(yep.getOrExecute()).toEqual(1) | |
}) | |
}) | |
describe('.tap()', () => { | |
it('Should be defined', () => { | |
expect(yep.tap).toBeDefined() | |
}) | |
it('Should allow us to execute a .just matching function', () => { | |
expect(yep.tap(tapMatch)).toEqual('Hey!') | |
}) | |
it('Should execute a .nothing matching function if the value becomes null or undefined', () => { | |
expect(yep.map((v) => (v = null)).tap(tapMatch)).toEqual('Putz!') | |
}) | |
}) | |
describe('.match() → alias of .tap()', () => { | |
it('Should be defined', () => { | |
expect(yep.match).toBeDefined() | |
}) | |
it('Should allow us to execute a .just matching function', () => { | |
expect(yep.match(tapMatch)).toEqual('Hey!') | |
}) | |
it('Should execute a .nothing matching function if the value becomes null or undefined', () => { | |
expect(yep.map((v) => (v = null)).match(tapMatch)).toEqual('Putz!') | |
}) | |
}) | |
}) | |
describe('Nothing', () => { | |
describe('.type', () => { | |
it('Should be defined', () => { | |
expect(nops.type).toBeDefined() | |
}) | |
it('Should be of Nothing type', () => { | |
expect(nops.type).toBe(MaybeType.Nothing) | |
}) | |
}) | |
describe('.has()', () => { | |
it('Should be defined', () => { | |
expect(nops.has).toBeDefined() | |
}) | |
it('Should return "false"', () => { | |
expect(nops.has()).toBeFalse() | |
}) | |
}) | |
describe('.isJust()', () => { | |
it('Should be defined', () => { | |
expect(nops.isJust).toBeDefined() | |
}) | |
it('Should return "true"', () => { | |
expect(nops.isJust()).toBeFalse() | |
}) | |
}) | |
describe('.isNothing()', () => { | |
it('Should be defined', () => { | |
expect(nops.isNothing).toBeDefined() | |
}) | |
it('Should return "false"', () => { | |
expect(nops.isNothing()).toBeTrue() | |
}) | |
}) | |
describe('.unwrap()', () => { | |
it('Should be defined', () => { | |
expect(nops.unwrap).toBeDefined() | |
}) | |
it('Should return "this"', () => { | |
let res = nops.unwrap() | |
expect(res.type).toBe(MaybeType.Nothing) | |
}) | |
}) | |
describe('.map()', () => { | |
it('Should be defined', () => { | |
expect(nops.map).toBeDefined() | |
}) | |
it('Should always return a Nothing type object', () => { | |
let res = nops.map((v) => (v = 1)) | |
expect(res.type).toBe(MaybeType.Nothing) | |
}) | |
}) | |
describe('.flatMap()', () => { | |
it('Should be defined', () => { | |
expect(nops.flatMap).toBeDefined() | |
}) | |
it('Should always return a Nothing type object', () => { | |
let res = nops.flatMap((v) => (v = 1)) | |
expect(res.type).toBe(MaybeType.Nothing) | |
}) | |
}) | |
describe('.filter()', () => { | |
it('Should be defined', () => { | |
expect(nops.filter).toBeDefined() | |
}) | |
it('Should always return a Nothing type object', () => { | |
let res = nops.filter((v) => typeof v === 'number') | |
expect(res.type).toBe(MaybeType.Nothing) | |
}) | |
}) | |
describe('.andThen()', () => { | |
it('Should be defined', () => { | |
expect(nops.andThen).toBeDefined() | |
}) | |
it('Should always return a Nothing type object', () => { | |
let res = nops.andThen(1000) | |
expect(res.type).toBe(MaybeType.Nothing) | |
}) | |
}) | |
describe('.and()', () => { | |
it('Should be defined', () => { | |
expect(nops.and).toBeDefined() | |
}) | |
it('Should always return a Nothing type object', () => { | |
let res = nops.and(1000) | |
expect(res.type).toBe(MaybeType.Nothing) | |
}) | |
}) | |
describe('.toString()', () => { | |
it('Should be defined', () => { | |
expect(nops.toString).toBeDefined() | |
}) | |
it('Should return a string representation of "this"', () => { | |
let res = nops.toString() | |
expect(typeof res).toBe('string') | |
expect(res).toEqual('Nothing()') | |
}) | |
}) | |
describe('.toArray()', () => { | |
it('Should be defined', () => { | |
expect(nops.toArray).toBeDefined() | |
}) | |
it('Should return an empty array', () => { | |
expect(nops.toArray()).toEqual([]) | |
}) | |
}) | |
describe('.valueOrUndefined()', () => { | |
it('Should be defined', () => { | |
expect(nops.valueOrUndefined).toBeDefined() | |
}) | |
it('Should return undefined', () => { | |
expect(nops.valueOrUndefined()).toEqual(undefined) | |
}) | |
}) | |
describe('.valueOrNull()', () => { | |
it('Should be defined', () => { | |
expect(nops.valueOrNull).toBeDefined() | |
}) | |
it('Should return null', () => { | |
expect(nops.valueOrNull()).toEqual(null) | |
}) | |
}) | |
describe('.or()', () => { | |
it('Should be defined', () => { | |
expect(nops.or).toBeDefined() | |
}) | |
it('Should return the provided option value', () => { | |
let res = nops.or('Bummer') | |
expect(typeof res).toBe('string') | |
expect(res).toEqual('Bummer') | |
}) | |
}) | |
describe('.getOrElse()', () => { | |
it('Should be defined', () => { | |
expect(nops.getOrElse).toBeDefined() | |
}) | |
it('Should return the provided getOrElse value', () => { | |
let res = nops.getOrElse('Else') | |
expect(typeof res).toBe('string') | |
expect(res).toEqual('Else') | |
}) | |
it('Should throw if called without a value', () => { | |
expect(() => nops.getOrElse()).toThrowError() | |
expect(() => nops.getOrElse(null)).toThrowError() | |
expect(() => nops.getOrElse(undefined)).toThrowError() | |
expect(() => nops.getOrElse('')).not.toThrowError() | |
expect(() => nops.getOrElse(0)).not.toThrowError() | |
expect(() => nops.getOrElse(false)).not.toThrowError() | |
}) | |
}) | |
describe('.getOrThrow()', () => { | |
it('Should be defined', () => { | |
expect(nops.getOrThrow).toBeDefined() | |
}) | |
it('Should throw the provided value or an error', () => { | |
expect(() => nops.getOrThrow(1)).toThrow() | |
expect(() => nops.getOrThrow(new Error('Ops?'))).toThrowError() | |
expect(() => nops.getOrThrow()).toThrowError() | |
}) | |
}) | |
describe('.getOrExecute()', () => { | |
it('Should be defined', () => { | |
expect(nops.getOrExecute).toBeDefined() | |
}) | |
it('Should throw if the provided argument is not a function', () => { | |
expect(() => nops.getOrExecute('1')).toThrowError() | |
}) | |
it('Should return the result of the provided function', () => { | |
expect(nops.getOrExecute(() => 1 + 1)).toEqual(2) | |
expect(nops.getOrExecute(() => 'Ops')).toEqual('Ops') | |
}) | |
}) | |
describe('.tap()', () => { | |
it('Should be defined', () => { | |
expect(nops.tap).toBeDefined() | |
}) | |
it('Should allow us to execute a .nothing matching function', () => { | |
expect(nops.tap(tapMatch)).toEqual('Putz!') | |
}) | |
it('Should always execute a .nothing matching function', () => { | |
expect(nops.tap(tapMatch)).toEqual('Putz!') | |
}) | |
}) | |
describe('.match() → alias of .tap()', () => { | |
it('Should be defined', () => { | |
expect(nops.match).toBeDefined() | |
}) | |
it('Should allow us to execute a .nothing matching function', () => { | |
expect(nops.match(tapMatch)).toEqual('Putz!') | |
}) | |
it('Should always execute a .nothing matching function', () => { | |
expect(nops.map((v) => (v = 1)).match(tapMatch)).toEqual('Putz!') | |
}) | |
}) | |
}) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const isNumberZero = (value) => typeof value === 'number' && value === 0 | |
const isFalse = (value) => typeof value === 'boolean' && !value | |
const isEmptyString = (value) => typeof value === 'string' && value === '' | |
const makeReadOnly = (object) => Object.freeze(object) | |
function notNullOrUndefined(value) { | |
if (isEmptyString(value)) return true | |
if (isNumberZero(value)) return true | |
if (isFalse(value)) return true | |
if (!value) return false | |
return typeof value !== null && typeof value !== undefined | |
} | |
function isValid(value) { | |
return ( | |
!!value || | |
isNumberZero(value) || | |
isFalse(value) || | |
isEmptyString(value) || | |
notNullOrUndefined(value) | |
) | |
} | |
module.exports = { isNumberZero, isFalse, isEmptyString, notNullOrUndefined, isValid, makeReadOnly } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment