Switched to deno
This commit is contained in:
parent
eea2d45951
commit
613b92adbd
|
@ -1,10 +1,9 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
[*.ts]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
end_of_line = lf
|
||||
max_line_length = off
|
||||
|
|
|
@ -1,5 +1 @@
|
|||
.venv
|
||||
node_modules
|
||||
.rts2_cache_*
|
||||
.coverage
|
||||
/dist/
|
||||
deno.d.ts
|
|
@ -1,11 +0,0 @@
|
|||
image: node:latest
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- node_modules/
|
||||
|
||||
test:
|
||||
before_script:
|
||||
- npm install
|
||||
script:
|
||||
- npm test
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
"deno.enable": true
|
||||
}
|
161
README.md
161
README.md
|
@ -1,161 +0,0 @@
|
|||
tk-functional
|
||||
=============
|
||||
|
||||
[![pipeline status](https://gitlab.com/thunderk/tk-functional/badges/master/pipeline.svg)](https://gitlab.com/thunderk/tk-functional/commits/master)
|
||||
[![coverage report](https://gitlab.com/thunderk/tk-functional/badges/master/coverage.svg)](https://gitlab.com/thunderk/tk-functional/commits/master)
|
||||
[![npm version](https://img.shields.io/npm/v/tk-functional.svg)](https://npmjs.com/tk-functional)
|
||||
[![npm size](https://img.shields.io/bundlephobia/min/tk-functional.svg)](https://bundlephobia.com/result?p=tk-functional)
|
||||
|
||||
About
|
||||
-----
|
||||
|
||||
Provides some common helpers for functional-style programming.
|
||||
|
||||
Typescript definitions are included.
|
||||
|
||||
Issues can be reported on [GitLab](https://gitlab.com/thunderk/tk-functional/issues).
|
||||
|
||||
Functions
|
||||
---------
|
||||
|
||||
**always** and **never** return true and false respectively
|
||||
|
||||
```typescript
|
||||
always() // => true
|
||||
never() // => false
|
||||
```
|
||||
|
||||
**and**, **or** and **not** allow to combine or negate predicates
|
||||
|
||||
```typescript
|
||||
const a = and((x: number) => x > 2, (x: number) => x < 5);
|
||||
a(2) // => false
|
||||
a(3) // => true
|
||||
a(4) // => true
|
||||
a(5) // => false
|
||||
const o = or((x: number) => x < 2, (x: number) => x > 3);
|
||||
o(1) // => true
|
||||
o(2) // => false
|
||||
o(3) // => false
|
||||
o(4) // => true
|
||||
const n = not((x: number) => x == 1);
|
||||
n(0) // => true
|
||||
n(1) // => false
|
||||
n(2) // => true
|
||||
```
|
||||
|
||||
**at** gets an array's item by position:
|
||||
|
||||
```typescript
|
||||
const getthird = at(2);
|
||||
getthird([2, 4, 8, 16]); // => 8
|
||||
getthird([2, 4]); // => undefined
|
||||
const getsecondlast = at(-2);
|
||||
getsecondlast([1, 2, 3, 4]); // => 3
|
||||
getsecondlast([1]); // => undefined
|
||||
```
|
||||
|
||||
**attr** gets an object's attribute:
|
||||
|
||||
```typescript
|
||||
const getx = attr("x");
|
||||
getx({x: 3}); // => 3
|
||||
```
|
||||
|
||||
**bool** checks for boolean equivalence (in a broader sense than !(!(val))):
|
||||
|
||||
```typescript
|
||||
bool(undefined); // => false
|
||||
bool(null); // => false
|
||||
|
||||
bool(-1); // => true
|
||||
bool(0); // => false
|
||||
bool(1); // => true
|
||||
|
||||
bool(""); // => false
|
||||
bool(" "); // => true
|
||||
bool("abc"); // => true
|
||||
|
||||
bool([]); // => false
|
||||
bool([1, 2, 3]); // => true
|
||||
|
||||
bool({}); // => false
|
||||
bool({x: 1}); // => true
|
||||
```
|
||||
|
||||
**cmp** simplifies the use of array sorting:
|
||||
|
||||
```typescript
|
||||
[8, 3, 5].sort(cmp()) // => [3, 5, 8]
|
||||
[8, 3, 5].sort(cmp({ reverse: true })) // => [8, 5, 3]
|
||||
[-2, 8, -7].sort(cmp({ key: Math.abs })) // => [-2, -7, 8]
|
||||
```
|
||||
|
||||
**first** and **last** return the first or last item of an array:
|
||||
|
||||
```typescript
|
||||
first([1, 2, 3]) // => 1
|
||||
first([]) // => undefined
|
||||
last([1, 2, 3]) // => 3
|
||||
last([]) // => undefined
|
||||
```
|
||||
|
||||
**identity** returns its argument untouched:
|
||||
|
||||
```typescript
|
||||
a === identity(a) // => true
|
||||
```
|
||||
|
||||
**is** and **isinstance** checks for strict equality and inheritance:
|
||||
|
||||
```typescript
|
||||
const f = is(8);
|
||||
f(8) // => true
|
||||
f(5 + 3) // => true
|
||||
f("8") // => false
|
||||
f(null) // => false
|
||||
|
||||
class A { }
|
||||
class A1 extends A { }
|
||||
class A2 extends A { }
|
||||
class B { }
|
||||
const f: any[] = [5, null, undefined, new A, new A1, new B, new A2];
|
||||
const result: A[] = f.filter(isinstance(A)); // => [f[3], f[4], f[6]]
|
||||
```
|
||||
|
||||
**nn**, **nu** and **nnu** checks at run-time for null or undefined:
|
||||
|
||||
```typescript
|
||||
nn(undefined) // => undefined
|
||||
nn(null) // => Error
|
||||
nn(1) // => 1
|
||||
|
||||
nu(undefined) // => Error
|
||||
nu(null) // => null
|
||||
nu(1) // => 1
|
||||
|
||||
nnu(undefined) // => Error
|
||||
nnu(null) // => Error
|
||||
nnu(1) // => 1
|
||||
```
|
||||
|
||||
**nop** does nothing (useful for some callbacks):
|
||||
|
||||
```typescript
|
||||
new ConstructorWithMandatoryCallback(nop)
|
||||
```
|
||||
|
||||
**partial** applies a partial configuration object as first argument of compatible functions:
|
||||
|
||||
```typescript
|
||||
const sum = (args: {a: number, b: number}) => args.a + args.b
|
||||
const plus1 = partial({a: 1}, sum);
|
||||
plus1({b: 8}) // => 9
|
||||
```
|
||||
|
||||
**pipe** chains two functions as one:
|
||||
|
||||
```typescript
|
||||
const f = pipe((x: number) => x * 2, (x: number) => x + 1);
|
||||
f(3) // => 7 ((3 * 2) + 1)
|
||||
```
|
|
@ -1,16 +0,0 @@
|
|||
# Activation script for virtual nodejs environment
|
||||
# Usage:
|
||||
# source activate_node
|
||||
|
||||
vdir="./.venv"
|
||||
expected="10.16.3"
|
||||
|
||||
if [ \! -f "./activate_node" ]
|
||||
then
|
||||
echo "Not in project directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
test -x "${vdir}/bin/nodeenv" || ( python3 -m venv "${vdir}" && "${vdir}/bin/pip" install --upgrade nodeenv )
|
||||
test "$(${vdir}/node/bin/nodejs --version)" = "v${expected}" || "${vdir}/bin/nodeenv" --node=${expected} --force "${vdir}/node"
|
||||
source "${vdir}/node/bin/activate"
|
|
@ -0,0 +1,6 @@
|
|||
export * from "./comparison.ts";
|
||||
export * from "./composition.ts";
|
||||
export * from "./iterables.ts";
|
||||
export * from "./objects.ts";
|
||||
export * from "./predicates.ts";
|
||||
export * from "./typing.ts";
|
|
@ -0,0 +1,58 @@
|
|||
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
|
||||
import { bool, cmp, is } from "./comparison.ts";
|
||||
|
||||
Deno.test("cmp", () => {
|
||||
assertEquals([8, 3, 5].sort(cmp()), [3, 5, 8]);
|
||||
assertEquals([8, 3, 5, 8].sort(cmp({ reverse: true })), [8, 8, 5, 3]);
|
||||
assertEquals([-2, 8, -7].sort(cmp({ key: Math.abs })), [-2, -7, 8]);
|
||||
assertEquals(["c", "a", "b"].sort(cmp()), ["a", "b", "c"]);
|
||||
});
|
||||
|
||||
Deno.test("is", () => {
|
||||
const f1 = is(5);
|
||||
assertEquals(f1(5), true);
|
||||
assertEquals(f1(4), false);
|
||||
assertEquals(f1("a" as any), false);
|
||||
assertEquals(f1(null as any), false);
|
||||
assertEquals(f1(undefined as any), false);
|
||||
|
||||
const obj: { x: number } = { x: 1 };
|
||||
let f2 = is(obj);
|
||||
assertEquals(f2(obj), true);
|
||||
assertEquals(f2({ x: 1 }), false);
|
||||
assertEquals(f2({ y: 1 } as any), false);
|
||||
});
|
||||
|
||||
Deno.test("bool", () => {
|
||||
assertEquals(bool(null), false);
|
||||
assertEquals(bool(undefined), false);
|
||||
|
||||
assertEquals(bool(false), false);
|
||||
assertEquals(bool(true), true);
|
||||
|
||||
assertEquals(bool(-1), true);
|
||||
assertEquals(bool(0), false);
|
||||
assertEquals(bool(1), true);
|
||||
|
||||
assertEquals(bool(""), false);
|
||||
assertEquals(bool(" "), true);
|
||||
assertEquals(bool("abc"), true);
|
||||
|
||||
assertEquals(bool([]), false);
|
||||
assertEquals(bool([0]), true);
|
||||
assertEquals(bool([1, 2, 3]), true);
|
||||
|
||||
assertEquals(bool(new Set()), false);
|
||||
assertEquals(bool(new Set([0])), true);
|
||||
assertEquals(bool(new Set([1, 2, 3])), true);
|
||||
|
||||
assertEquals(bool({}), false);
|
||||
assertEquals(bool({ a: 5 }), true);
|
||||
|
||||
class Obj1 {}
|
||||
class Obj2 {
|
||||
private x = 0;
|
||||
}
|
||||
assertEquals(bool(new Obj1()), false);
|
||||
assertEquals(bool(new Obj2()), true);
|
||||
});
|
|
@ -0,0 +1,46 @@
|
|||
import { identity } from "./composition.ts";
|
||||
|
||||
type cmpArgs = Readonly<{ key: (item: any) => any; reverse: boolean }>;
|
||||
const cmpDefaults: cmpArgs = { key: identity, reverse: false };
|
||||
/**
|
||||
* Compare operator, that can be used in sort() calls.
|
||||
*/
|
||||
export function cmp(args?: Partial<cmpArgs>): (a: any, b: any) => number {
|
||||
const fargs = { ...cmpDefaults, ...args };
|
||||
return (a, b) => {
|
||||
const ka = fargs.key(a);
|
||||
const kb = fargs.key(b);
|
||||
if (ka > kb) {
|
||||
return fargs.reverse ? -1 : 1;
|
||||
} else if (ka < kb) {
|
||||
return fargs.reverse ? 1 : -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a function to check the strict equality with a reference
|
||||
*/
|
||||
export function is<T, S extends T>(
|
||||
ref: T,
|
||||
): (input: S) => input is Extract<T, S> {
|
||||
return (x): x is Extract<T, S> => x === ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a value to a boolean (are considered falsy: 0, false, "", {}, [], null, undefined)
|
||||
*/
|
||||
export function bool<T>(value: T | null | undefined): value is T;
|
||||
export function bool(value: any): boolean {
|
||||
if (!value) {
|
||||
return false;
|
||||
} else if (value instanceof Set) {
|
||||
return value.size > 0;
|
||||
} else if (typeof value == "object") {
|
||||
return Object.keys(value).length > 0;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
|
||||
import { identity, nop, partial, pipe } from "./composition.ts";
|
||||
|
||||
Deno.test("nop", () => {
|
||||
assertEquals(nop(), undefined);
|
||||
});
|
||||
|
||||
Deno.test("identity", () => {
|
||||
assertEquals(identity(5), 5);
|
||||
assertEquals(identity(null), null);
|
||||
});
|
||||
|
||||
Deno.test("partial", () => {
|
||||
const func1 = (conf: { a: number; b: string; c: number }) =>
|
||||
`${conf.a}${conf.b}${conf.c}`;
|
||||
const pfunc1 = partial({ b: "test" }, func1);
|
||||
assertEquals(pfunc1({ a: 5, c: 8 }), "5test8");
|
||||
|
||||
const func2 = (conf: { a: number; b: string }, c: number) =>
|
||||
`${conf.a}${conf.b}${c}`;
|
||||
const pfunc2 = partial({ b: "test" }, func2);
|
||||
assertEquals(pfunc2({ a: 2 }, 3), "2test3");
|
||||
});
|
||||
|
||||
Deno.test("pipe", () => {
|
||||
const f = pipe((x: number) => x * 2, (x: number) => x + 1);
|
||||
assertEquals(f(1), 3);
|
||||
assertEquals(f(2), 5);
|
||||
assertEquals(f(3), 7);
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* Functions that does nothing (useful for default callbacks)
|
||||
*/
|
||||
export function nop(...args: any[]): any {
|
||||
}
|
||||
|
||||
/**
|
||||
* Identity function (returns the sole argument untouched)
|
||||
*/
|
||||
export function identity<T>(input: T): T {
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Partially apply fixed arguments to a function with named arguments
|
||||
*/
|
||||
export function partial<
|
||||
A extends object,
|
||||
PA extends Partial<A>,
|
||||
RA extends any[],
|
||||
R,
|
||||
>(
|
||||
fixedargs: PA,
|
||||
func: (args: A, ...rest: RA) => R,
|
||||
): (args: Omit<A, keyof PA>, ...rest: RA) => R {
|
||||
return (args: Omit<A, keyof PA>, ...rest: RA) =>
|
||||
func({ ...fixedargs, ...args } as any, ...rest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pipe the result of a function as first parameter of another, to create a new function
|
||||
*/
|
||||
export function pipe<IN extends any[], INT, R>(
|
||||
f1: (...args: IN) => INT,
|
||||
f2: (value: INT) => R,
|
||||
): (...args: IN) => R {
|
||||
return (...args: IN) => f2(f1(...args));
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
|
||||
import { at, first, fmap, last, second, third } from "./iterables.ts";
|
||||
|
||||
Deno.test("at", () => {
|
||||
const second = at(1);
|
||||
assertEquals(second([1, 4, 8]), 4);
|
||||
assertEquals(second([1]), undefined);
|
||||
|
||||
const second_from_end = at(-2);
|
||||
assertEquals(second_from_end([1, 4, 6, 8]), 6);
|
||||
assertEquals(second_from_end([1]), undefined);
|
||||
});
|
||||
|
||||
Deno.test("first", () => {
|
||||
assertEquals(first([1, 4, 8]), 1);
|
||||
assertEquals(first([1]), 1);
|
||||
assertEquals(first([]), undefined);
|
||||
});
|
||||
|
||||
Deno.test("second", () => {
|
||||
assertEquals(second([1, 4, 8]), 4);
|
||||
assertEquals(second([1]), undefined);
|
||||
assertEquals(second([]), undefined);
|
||||
});
|
||||
|
||||
Deno.test("third", () => {
|
||||
assertEquals(third([1, 4, 8]), 8);
|
||||
assertEquals(third([1, 4]), undefined);
|
||||
assertEquals(third([]), undefined);
|
||||
});
|
||||
|
||||
Deno.test("last", () => {
|
||||
assertEquals(last([1, 4, 8]), 8);
|
||||
assertEquals(last([1]), 1);
|
||||
assertEquals(last([]), undefined);
|
||||
});
|
||||
|
||||
Deno.test("fmap", () => {
|
||||
assertEquals(fmap()([1, 2, 3]), [1, 2, 3]);
|
||||
assertEquals(fmap((x: number) => x * 2)([1, 2, 3]), [2, 4, 6]);
|
||||
assertEquals(fmap(undefined, (x: number) => x % 2 == 0)([1, 2, 3]), [2]);
|
||||
assertEquals(fmap((x: number) => x * 2)([1, 2, 3]), [2, 4, 6]);
|
||||
assertEquals(
|
||||
fmap((x: number) => x * 2, (x: number) => x < 5)([1, 2, 3]),
|
||||
[2, 4],
|
||||
);
|
||||
});
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* Index getter
|
||||
*/
|
||||
export function at<T>(idx: number): (array: ReadonlyArray<T>) => T | undefined {
|
||||
if (idx < 0) {
|
||||
return (array) => array[array.length + idx];
|
||||
} else {
|
||||
return (array) => array[idx];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Array first item getter
|
||||
*/
|
||||
export const first: <T extends ReadonlyArray<any>>(
|
||||
array: T,
|
||||
) => T[0] | undefined = at(0);
|
||||
|
||||
/**
|
||||
* Array second item getter
|
||||
*/
|
||||
export const second: <T extends ReadonlyArray<any>>(
|
||||
array: T,
|
||||
) => T[1] | undefined = at(1);
|
||||
|
||||
/**
|
||||
* Array third item getter
|
||||
*/
|
||||
export const third: <T extends ReadonlyArray<any>>(
|
||||
array: T,
|
||||
) => T[2] | undefined = at(2);
|
||||
|
||||
/**
|
||||
* Array last item getter
|
||||
*/
|
||||
export const last: <T>(array: ReadonlyArray<T>) => T | undefined = at(-1);
|
||||
|
||||
/**
|
||||
* Applies map and filter on an array, using a single function
|
||||
*/
|
||||
export function fmap<A, T extends A>(
|
||||
m?: undefined,
|
||||
f?: (val: A) => val is T,
|
||||
): (array: A[]) => T[];
|
||||
export function fmap<A>(
|
||||
m?: undefined,
|
||||
f?: (val: A) => boolean,
|
||||
): (array: A[]) => A[];
|
||||
export function fmap<A, B, T extends B>(
|
||||
m: (val: A) => B,
|
||||
f?: (val: B) => val is T,
|
||||
): (array: A[]) => T[];
|
||||
export function fmap<A, B>(
|
||||
m: (val: A) => B,
|
||||
f?: (val: B) => boolean,
|
||||
): (array: A[]) => B[];
|
||||
export function fmap<A, B>(
|
||||
m?: (val: A) => B,
|
||||
f?: (val: A | B) => boolean,
|
||||
): (array: A[]) => (A | B)[] {
|
||||
return (array) => {
|
||||
if (m && f) {
|
||||
return array.map(m).filter(f);
|
||||
} else if (m) {
|
||||
return array.map(m);
|
||||
} else if (f) {
|
||||
return array.filter(f);
|
||||
} else {
|
||||
return array.slice();
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
module.exports = {
|
||||
transform: {
|
||||
"^.+\\.ts$": "ts-jest"
|
||||
},
|
||||
moduleFileExtensions: [
|
||||
"ts",
|
||||
"js",
|
||||
"json",
|
||||
"node"
|
||||
],
|
||||
restoreMocks: true,
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: [
|
||||
"src/**/*.ts",
|
||||
"!src/**/*.test.ts",
|
||||
],
|
||||
coverageDirectory: ".coverage",
|
||||
coverageReporters: [
|
||||
"lcovonly",
|
||||
"html",
|
||||
"text-summary"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
|
||||
import { attr } from "./objects.ts";
|
||||
|
||||
Deno.test("attr", () => {
|
||||
const getx = attr("x");
|
||||
assertEquals(getx({ x: 4, y: 5 }), 4);
|
||||
assertEquals(getx({ x: undefined, y: 5 }), undefined);
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
/**
|
||||
* Attribute getter
|
||||
*/
|
||||
export function attr<K extends string>(
|
||||
name: K,
|
||||
): <O extends Record<K, O[K]>>(obj: O) => O[K] {
|
||||
return (obj) => obj[name];
|
||||
}
|
File diff suppressed because it is too large
Load Diff
44
package.json
44
package.json
|
@ -1,44 +0,0 @@
|
|||
{
|
||||
"name": "tk-functional",
|
||||
"version": "0.2.4",
|
||||
"description": "Typescript/Javascript helpers for functional-style programming",
|
||||
"source": "src/index.ts",
|
||||
"main": "dist/tk-functional.umd.js",
|
||||
"module": "dist/tk-functional.modern.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"/src",
|
||||
"/dist"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
"normalize": "tk-base",
|
||||
"build": "microbundle build -f modern,umd",
|
||||
"prepare": "npm run build",
|
||||
"prepublishOnly": "npm test",
|
||||
"dev": "run-p dev:*",
|
||||
"dev:test": "jest --watchAll",
|
||||
"dev:build": "microbundle watch -f modern,umd",
|
||||
"dev:serve": "live-server --host=localhost --port=5000 --no-browser dist"
|
||||
},
|
||||
"author": {
|
||||
"name": "Michaël Lemaire",
|
||||
"url": "https://thunderk.net"
|
||||
},
|
||||
"license": "ISC",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://code.thunderk.net/tslib/tk-functional.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://gitlab.com/thunderk/tk-functional/issues"
|
||||
},
|
||||
"homepage": "https://code.thunderk.net/tslib/tk-functional",
|
||||
"keywords": [
|
||||
"typescript"
|
||||
],
|
||||
"devDependencies": {
|
||||
"tk-base": "^0.2.5"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
|
||||
import { always, and, never, not, or } from "./predicates.ts";
|
||||
|
||||
Deno.test("always", () => {
|
||||
assertEquals(always(), true);
|
||||
assertEquals(always(true), true);
|
||||
assertEquals(always(false), true);
|
||||
});
|
||||
|
||||
Deno.test("never", () => {
|
||||
assertEquals(never(), false);
|
||||
assertEquals(never(true), false);
|
||||
assertEquals(never(false), false);
|
||||
});
|
||||
|
||||
Deno.test("not", () => {
|
||||
const iseven = (x: number) => x % 2 == 0;
|
||||
const isodd = not(iseven);
|
||||
assertEquals(iseven(0), true);
|
||||
assertEquals(isodd(0), false);
|
||||
assertEquals(iseven(1), false);
|
||||
assertEquals(isodd(1), true);
|
||||
assertEquals(iseven(2), true);
|
||||
assertEquals(isodd(2), false);
|
||||
});
|
||||
|
||||
Deno.test("and", () => {
|
||||
const f = and((x: number) => x % 2 == 0, (x: number) => x > 5);
|
||||
assertEquals(f(0), false);
|
||||
assertEquals(f(2), false);
|
||||
assertEquals(f(8), true);
|
||||
assertEquals(f(9), false);
|
||||
assertEquals(f(10), true);
|
||||
});
|
||||
|
||||
Deno.test("or", () => {
|
||||
const f = or((x: number) => x % 2 == 0, (x: number) => x > 5);
|
||||
assertEquals(f(0), true);
|
||||
assertEquals(f(1), false);
|
||||
assertEquals(f(2), true);
|
||||
assertEquals(f(8), true);
|
||||
assertEquals(f(9), true);
|
||||
});
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* Predicate that always returns true
|
||||
*/
|
||||
export const always = (...args: any[]) => true;
|
||||
|
||||
/**
|
||||
* Predicate that always returns false
|
||||
*/
|
||||
export const never = (...args: any[]) => false;
|
||||
|
||||
/**
|
||||
* Negate a predicate
|
||||
*/
|
||||
export function not<A extends any[]>(
|
||||
predicate: (...args: A) => boolean,
|
||||
): (...args: A) => boolean {
|
||||
return (...args) => !predicate(...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a boolean "and" to merge predicates
|
||||
*/
|
||||
export function and<A extends any[]>(
|
||||
...predicates: ((...args: A) => boolean)[]
|
||||
): (...args: A) => boolean {
|
||||
return (...args) => {
|
||||
for (let p of predicates) {
|
||||
if (!p(...args)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a boolean "or" to merge predicates
|
||||
*/
|
||||
export function or<A extends any[]>(
|
||||
...predicates: ((...args: A) => boolean)[]
|
||||
): (...args: A) => boolean {
|
||||
return (...args) => {
|
||||
for (let p of predicates) {
|
||||
if (p(...args)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
|
@ -1,257 +0,0 @@
|
|||
import { always, and, at, attr, bool, cmp, first, fmap, identity, is, isinstance, last, never, nn, nnu, nop, not, nu, or, partial, pipe, second, third } from "./index";
|
||||
|
||||
describe(nop, () => {
|
||||
it("does nothing", () => {
|
||||
expect(nop()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe(identity, () => {
|
||||
it("returns the argument untouched", () => {
|
||||
expect(identity(5)).toBe(5);
|
||||
expect(identity(null)).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe(partial, () => {
|
||||
it("applies systematically fixed configuration", () => {
|
||||
const func = (conf: { a: number, b: string, c: number }) => `${conf.a}${conf.b}${conf.c}`;
|
||||
const pfunc = partial({ b: "test" }, func);
|
||||
expect(pfunc({ a: 5, c: 8 })).toEqual("5test8");
|
||||
});
|
||||
|
||||
it("pass through remaining arguments", () => {
|
||||
const func = (conf: { a: number, b: string }, c: number) => `${conf.a}${conf.b}${c}`;
|
||||
const pfunc = partial({ b: "test" }, func);
|
||||
expect(pfunc({ a: 2 }, 3)).toEqual("2test3");
|
||||
});
|
||||
});
|
||||
|
||||
describe(cmp, () => {
|
||||
it("simplifies array sorting", () => {
|
||||
expect([8, 3, 5].sort(cmp())).toEqual([3, 5, 8]);
|
||||
expect([8, 3, 5, 8].sort(cmp({ reverse: true }))).toEqual([8, 8, 5, 3]);
|
||||
expect([-2, 8, -7].sort(cmp({ key: Math.abs }))).toEqual([-2, -7, 8]);
|
||||
expect(["c", "a", "b"].sort(cmp())).toEqual(["a", "b", "c"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe(always, () => {
|
||||
it("returns true", () => {
|
||||
expect(always()).toBe(true);
|
||||
expect(always(true)).toBe(true);
|
||||
expect(always(false)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe(never, () => {
|
||||
it("returns false", () => {
|
||||
expect(never()).toBe(false);
|
||||
expect(never(true)).toBe(false);
|
||||
expect(never(false)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe(not, () => {
|
||||
it("negates predicate", () => {
|
||||
const iseven = (x: number) => x % 2 == 0;
|
||||
const isodd = not(iseven);
|
||||
expect(iseven(0)).toBe(true);
|
||||
expect(isodd(0)).toBe(false);
|
||||
expect(iseven(1)).toBe(false);
|
||||
expect(isodd(1)).toBe(true);
|
||||
expect(iseven(2)).toBe(true);
|
||||
expect(isodd(2)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe(and, () => {
|
||||
it("combines predicates", () => {
|
||||
const f = and((x: number) => x % 2 == 0, (x: number) => x > 5);
|
||||
expect(f(0)).toBe(false);
|
||||
expect(f(2)).toBe(false);
|
||||
expect(f(8)).toBe(true);
|
||||
expect(f(9)).toBe(false);
|
||||
expect(f(10)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe(or, () => {
|
||||
it("combines predicates", () => {
|
||||
const f = or((x: number) => x % 2 == 0, (x: number) => x > 5);
|
||||
expect(f(0)).toBe(true);
|
||||
expect(f(1)).toBe(false);
|
||||
expect(f(2)).toBe(true);
|
||||
expect(f(8)).toBe(true);
|
||||
expect(f(9)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe(is, () => {
|
||||
it("checks for equality", () => {
|
||||
const f = is(5);
|
||||
expect(f(5)).toBe(true);
|
||||
expect(f(4)).toBe(false);
|
||||
expect(f("a" as any)).toBe(false);
|
||||
expect(f(null as any)).toBe(false);
|
||||
expect(f(undefined as any)).toBe(false);
|
||||
});
|
||||
|
||||
it("applies strict equality", () => {
|
||||
const obj: { x: number } = { x: 1 };
|
||||
let f = is(obj);
|
||||
expect(f(obj)).toBe(true);
|
||||
expect(f({ x: 1 })).toBe(false);
|
||||
expect(f({ y: 1 } as any)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe(isinstance, () => {
|
||||
it("checks for subclasses", () => {
|
||||
class A { }
|
||||
class A1 extends A { }
|
||||
class A2 extends A { }
|
||||
class B { }
|
||||
|
||||
const f: any[] = [5, null, undefined, new A, new A1, new B, new A2];
|
||||
const result = f.filter(isinstance(A));
|
||||
expect(result).toEqual([f[3], f[4], f[6]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe(pipe, () => {
|
||||
it("pipes two functions", () => {
|
||||
const f = pipe((x: number) => x * 2, (x: number) => x + 1);
|
||||
expect(f(1)).toBe(3);
|
||||
expect(f(2)).toBe(5);
|
||||
expect(f(3)).toBe(7);
|
||||
});
|
||||
});
|
||||
|
||||
describe(attr, () => {
|
||||
it("gets an attribute from an object", () => {
|
||||
const getx = attr("x");
|
||||
expect(getx({ x: 4, y: 5 })).toBe(4);
|
||||
expect(getx({ x: undefined, y: 5 })).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe(at, () => {
|
||||
it("gets an index in an array", () => {
|
||||
const second = at(1);
|
||||
expect(second([1, 4, 8])).toBe(4);
|
||||
expect(second([1])).toBeUndefined();
|
||||
});
|
||||
|
||||
it("handles negative indices from the end", () => {
|
||||
const second_from_end = at(-2);
|
||||
expect(second_from_end([1, 4, 6, 8])).toBe(6);
|
||||
expect(second_from_end([1])).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe(first, () => {
|
||||
it("gets the first item in an array", () => {
|
||||
expect(first([1, 4, 8])).toBe(1);
|
||||
expect(first([1])).toBe(1);
|
||||
expect(first([])).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe(second, () => {
|
||||
it("gets the second item in an array", () => {
|
||||
expect(second([1, 4, 8])).toBe(4);
|
||||
expect(second([1])).toBeUndefined();
|
||||
expect(second([])).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe(third, () => {
|
||||
it("gets the third item in an array", () => {
|
||||
expect(third([1, 4, 8])).toBe(8);
|
||||
expect(third([1, 4])).toBeUndefined();
|
||||
expect(third([])).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe(last, () => {
|
||||
it("gets the last item in an array", () => {
|
||||
expect(last([1, 4, 8])).toBe(8);
|
||||
expect(last([1])).toBe(1);
|
||||
expect(last([])).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe(nn, () => {
|
||||
it("checks for null", () => {
|
||||
expect(nn(undefined)).toBeUndefined();
|
||||
expect(nn(0)).toBe(0);
|
||||
expect(nn("")).toBe("");
|
||||
expect(nn([])).toEqual([]);
|
||||
expect(() => nn(null)).toThrowError("null value");
|
||||
});
|
||||
});
|
||||
|
||||
describe(nu, () => {
|
||||
it("checks for undefined", () => {
|
||||
expect(nu(null)).toBe(null);
|
||||
expect(nu(0)).toBe(0);
|
||||
expect(nu("")).toBe("");
|
||||
expect(nu([])).toEqual([]);
|
||||
expect(() => nu(undefined)).toThrowError("undefined value");
|
||||
});
|
||||
});
|
||||
|
||||
describe(nnu, () => {
|
||||
it("checks for null or undefined", () => {
|
||||
expect(nnu(0)).toBe(0);
|
||||
expect(nnu("")).toBe("");
|
||||
expect(nnu([])).toEqual([]);
|
||||
expect(() => nnu(undefined)).toThrowError("undefined value");
|
||||
expect(() => nnu(null)).toThrowError("null value");
|
||||
});
|
||||
});
|
||||
|
||||
describe(bool, () => {
|
||||
it("checks for boolean equivalent", () => {
|
||||
expect(bool(null)).toBe(false);
|
||||
expect(bool(undefined)).toBe(false);
|
||||
|
||||
expect(bool(false)).toBe(false);
|
||||
expect(bool(true)).toBe(true);
|
||||
|
||||
expect(bool(-1)).toBe(true);
|
||||
expect(bool(0)).toBe(false);
|
||||
expect(bool(1)).toBe(true);
|
||||
|
||||
expect(bool("")).toBe(false);
|
||||
expect(bool(" ")).toBe(true);
|
||||
expect(bool("abc")).toBe(true);
|
||||
|
||||
expect(bool([])).toBe(false);
|
||||
expect(bool([0])).toBe(true);
|
||||
expect(bool([1, 2, 3])).toBe(true);
|
||||
|
||||
expect(bool(new Set())).toBe(false);
|
||||
expect(bool(new Set([0]))).toBe(true);
|
||||
expect(bool(new Set([1, 2, 3]))).toBe(true);
|
||||
|
||||
expect(bool({})).toBe(false);
|
||||
expect(bool({ a: 5 })).toBe(true);
|
||||
|
||||
class Obj1 { };
|
||||
class Obj2 { private x = 0 };
|
||||
expect(bool(new Obj1())).toBe(false);
|
||||
expect(bool(new Obj2())).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe(fmap, () => {
|
||||
it("applies map and filter on an array", () => {
|
||||
expect(fmap()([1, 2, 3])).toEqual([1, 2, 3]);
|
||||
expect(fmap((x: number) => x * 2)([1, 2, 3])).toEqual([2, 4, 6]);
|
||||
expect(fmap(undefined, (x: number) => x % 2 == 0)([1, 2, 3])).toEqual([2]);
|
||||
expect(fmap((x: number) => x * 2)([1, 2, 3])).toEqual([2, 4, 6]);
|
||||
expect(fmap((x: number) => x * 2, (x: number) => x < 5)([1, 2, 3])).toEqual([2, 4]);
|
||||
});
|
||||
});
|
207
src/index.ts
207
src/index.ts
|
@ -1,207 +0,0 @@
|
|||
/**
|
||||
* Functions that does nothing (useful for default callbacks)
|
||||
*/
|
||||
export function nop(...args: any[]): any {
|
||||
}
|
||||
|
||||
/**
|
||||
* Identity function (returns the sole argument untouched)
|
||||
*/
|
||||
export function identity<T>(input: T): T {
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Partially apply fixed arguments to a function with named arguments
|
||||
*/
|
||||
export function partial<A extends object, PA extends Partial<A>, RA extends any[], R>(fixedargs: PA, func: (args: A, ...rest: RA) => R): (args: Omit<A, keyof PA>, ...rest: RA) => R {
|
||||
return (args: Omit<A, keyof PA>, ...rest: RA) => func({ ...fixedargs, ...args } as any, ...rest);
|
||||
}
|
||||
|
||||
type cmpArgs = Readonly<{ key: (item: any) => any, reverse: boolean }>
|
||||
const cmpDefaults: cmpArgs = { key: identity, reverse: false }
|
||||
/**
|
||||
* Compare operator, that can be used in sort() calls.
|
||||
*/
|
||||
export function cmp(args?: Partial<cmpArgs>): (a: any, b: any) => number {
|
||||
const fargs = { ...cmpDefaults, ...args };
|
||||
return (a, b) => {
|
||||
const ka = fargs.key(a);
|
||||
const kb = fargs.key(b);
|
||||
if (ka > kb) {
|
||||
return fargs.reverse ? -1 : 1;
|
||||
} else if (ka < kb) {
|
||||
return fargs.reverse ? 1 : -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate that always returns true
|
||||
*/
|
||||
export const always = (...args: any[]) => true
|
||||
|
||||
/**
|
||||
* Predicate that always returns false
|
||||
*/
|
||||
export const never = (...args: any[]) => false
|
||||
|
||||
/**
|
||||
* Negate a predicate
|
||||
*/
|
||||
export function not<A extends any[]>(predicate: (...args: A) => boolean): (...args: A) => boolean {
|
||||
return (...args) => !predicate(...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a boolean "and" to merge predicates
|
||||
*/
|
||||
export function and<A extends any[]>(...predicates: ((...args: A) => boolean)[]): (...args: A) => boolean {
|
||||
return (...args) => {
|
||||
for (let p of predicates) {
|
||||
if (!p(...args)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a boolean "or" to merge predicates
|
||||
*/
|
||||
export function or<A extends any[]>(...predicates: ((...args: A) => boolean)[]): (...args: A) => boolean {
|
||||
return (...args) => {
|
||||
for (let p of predicates) {
|
||||
if (p(...args)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a function to check the strict equality with a reference
|
||||
*/
|
||||
export function is<T, S extends T>(ref: T): (input: S) => input is Extract<T, S> {
|
||||
return (x): x is Extract<T, S> => x === ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a function to check that inputs are instances of a given type
|
||||
*/
|
||||
export function isinstance<T, S extends T>(ref: { new(...args: any[]): T }): (input: S) => input is Extract<T, S> {
|
||||
return (x): x is Extract<T, S> => x instanceof ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pipe the result of a function as first parameter of another, to create a new function
|
||||
*/
|
||||
export function pipe<IN extends any[], INT, R>(f1: (...args: IN) => INT, f2: (value: INT) => R): (...args: IN) => R {
|
||||
return (...args: IN) => f2(f1(...args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attribute getter
|
||||
*/
|
||||
export function attr<K extends string>(name: K): <O extends Record<K, O[K]>>(obj: O) => O[K] {
|
||||
return obj => obj[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Index getter
|
||||
*/
|
||||
export function at<T>(idx: number): (array: ReadonlyArray<T>) => T | undefined {
|
||||
if (idx < 0) {
|
||||
return array => array[array.length + idx];
|
||||
} else {
|
||||
return array => array[idx];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Array first item getter
|
||||
*/
|
||||
export const first: <T extends ReadonlyArray<any>>(array: T) => T[0] | undefined = at(0);
|
||||
|
||||
/**
|
||||
* Array second item getter
|
||||
*/
|
||||
export const second: <T extends ReadonlyArray<any>>(array: T) => T[1] | undefined = at(1);
|
||||
|
||||
/**
|
||||
* Array third item getter
|
||||
*/
|
||||
export const third: <T extends ReadonlyArray<any>>(array: T) => T[2] | undefined = at(2);
|
||||
|
||||
/**
|
||||
* Array last item getter
|
||||
*/
|
||||
export const last: <T>(array: ReadonlyArray<T>) => T | undefined = at(-1);
|
||||
|
||||
/**
|
||||
* Check that a value is not null, throwing an error if it is
|
||||
*/
|
||||
export function nn<T>(value: T | null): T {
|
||||
if (value === null) {
|
||||
throw new Error("null value");
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a value is not undefined, throwing an error if it is
|
||||
*/
|
||||
export function nu<T>(value: T | undefined): T {
|
||||
if (typeof value === "undefined") {
|
||||
throw new Error("undefined value");
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a value is not null nor undefined, throwing an error if it is
|
||||
*/
|
||||
export const nnu: <T>(value: T | null | undefined) => T = pipe(nn, nu);
|
||||
|
||||
/**
|
||||
* Convert a value to a boolean (are considered falsy: 0, false, "", {}, [], null, undefined)
|
||||
*/
|
||||
export function bool<T>(value: T | null | undefined): value is T;
|
||||
export function bool(value: any): boolean {
|
||||
if (!value) {
|
||||
return false;
|
||||
} else if (value instanceof Set) {
|
||||
return value.size > 0;
|
||||
} else if (typeof value == "object") {
|
||||
return Object.keys(value).length > 0;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies map and filter on an array, using a single function
|
||||
*/
|
||||
export function fmap<A, T extends A>(m?: undefined, f?: (val: A) => val is T): (array: A[]) => T[];
|
||||
export function fmap<A>(m?: undefined, f?: (val: A) => boolean): (array: A[]) => A[];
|
||||
export function fmap<A, B, T extends B>(m: (val: A) => B, f?: (val: B) => val is T): (array: A[]) => T[];
|
||||
export function fmap<A, B>(m: (val: A) => B, f?: (val: B) => boolean): (array: A[]) => B[];
|
||||
export function fmap<A, B>(m?: (val: A) => B, f?: (val: A | B) => boolean): (array: A[]) => (A | B)[] {
|
||||
return array => {
|
||||
if (m && f) {
|
||||
return array.map(m).filter(f);
|
||||
} else if (m) {
|
||||
return array.map(m);
|
||||
} else if (f) {
|
||||
return array.filter(f);
|
||||
} else {
|
||||
return array.slice();
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,21 +1,10 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"target": "ESNext",
|
||||
"strict": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"preserveConstEnums": true,
|
||||
"declaration": true,
|
||||
"target": "es6",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es6"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"src/**/*.test.ts"
|
||||
]
|
||||
"preserveConstEnums": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
import {
|
||||
assertEquals,
|
||||
assertThrows,
|
||||
} from "https://deno.land/std/testing/asserts.ts";
|
||||
import { isinstance, nn, nnu, nu } from "./typing.ts";
|
||||
|
||||
Deno.test("isinstance", () => {
|
||||
class A {}
|
||||
class A1 extends A {}
|
||||
class A2 extends A {}
|
||||
class B {}
|
||||
|
||||
const f: any[] = [5, null, undefined, new A(), new A1(), new B(), new A2()];
|
||||
const result = f.filter(isinstance(A));
|
||||
assertEquals(result, [f[3], f[4], f[6]]);
|
||||
});
|
||||
|
||||
Deno.test("nn", () => {
|
||||
assertEquals(nn(undefined), undefined);
|
||||
assertEquals(nn(0), 0);
|
||||
assertEquals(nn(""), "");
|
||||
assertEquals(nn([]), []);
|
||||
assertThrows(() => nn(null), Error, "null value");
|
||||
});
|
||||
|
||||
Deno.test("nu", () => {
|
||||
assertEquals(nu(null), null);
|
||||
assertEquals(nu(0), 0);
|
||||
assertEquals(nu(""), "");
|
||||
assertEquals(nu([]), []);
|
||||
assertThrows(() => nu(undefined), Error, "undefined value");
|
||||
});
|
||||
|
||||
Deno.test("nnu", () => {
|
||||
assertEquals(nnu(0), 0);
|
||||
assertEquals(nnu(""), "");
|
||||
assertEquals(nnu([]), []);
|
||||
assertThrows(() => nnu(undefined), Error, "undefined value");
|
||||
assertThrows(() => nnu(null), Error, "null value");
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
import { pipe } from "./composition.ts";
|
||||
|
||||
/**
|
||||
* Get a function to check that inputs are instances of a given type
|
||||
*/
|
||||
export function isinstance<T, S extends T>(
|
||||
ref: { new (...args: any[]): T },
|
||||
): (input: S) => input is Extract<T, S> {
|
||||
return (x): x is Extract<T, S> => x instanceof ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a value is not null, throwing an error if it is
|
||||
*/
|
||||
export function nn<T>(value: T | null): T {
|
||||
if (value === null) {
|
||||
throw new Error("null value");
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a value is not undefined, throwing an error if it is
|
||||
*/
|
||||
export function nu<T>(value: T | undefined): T {
|
||||
if (typeof value === "undefined") {
|
||||
throw new Error("undefined value");
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a value is not null nor undefined, throwing an error if it is
|
||||
*/
|
||||
export const nnu: <T>(value: T | null | undefined) => T = pipe(nn, nu);
|
Loading…
Reference in New Issue