Add defined() and options()
This commit is contained in:
parent
aae9949650
commit
6395cdbacb
16 changed files with 433 additions and 11 deletions
178
README.md
178
README.md
|
@ -1,3 +1,181 @@
|
|||
# typescript/functional
|
||||
|
||||
[![Build Status](https://thunderk.visualstudio.com/typescript/_apis/build/status/functional?branchName=master)](https://dev.azure.com/thunderk/typescript/_build?pipelineNameFilter=functional)
|
||||
|
||||
## About
|
||||
|
||||
Provides some common helpers for functional-style programming.
|
||||
|
||||
## Import
|
||||
|
||||
In deno:
|
||||
|
||||
```typescript
|
||||
import { bool } from "https://js.thunderk.net/functional/mod.ts";
|
||||
```
|
||||
|
||||
In browser:
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
import { bool } from "https://js.thunderk.net/functional/mod.js";
|
||||
</script>
|
||||
```
|
||||
|
||||
## Use
|
||||
|
||||
**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]
|
||||
```
|
||||
|
||||
**defined** removes undefined values from an object:
|
||||
|
||||
```typescript
|
||||
defined({ a: 1, b: undefined }); // => {a: 1}
|
||||
```
|
||||
|
||||
**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);
|
||||
```
|
||||
|
||||
**options** merges options over default values:
|
||||
|
||||
```typescript
|
||||
options({ a: 1, b: 2, c: 3, d: 4 }, { a: 11, c: 33 }, { b: 22 });
|
||||
// => {a: 11, b: 22, c: 33, d: 4}
|
||||
```
|
||||
|
||||
**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,5 +1,5 @@
|
|||
import { bool, cmp, is } from "./comparison.ts";
|
||||
import { expect, it } from "./deps.test.ts";
|
||||
import { expect, it } from "./deps.testing.ts";
|
||||
|
||||
it("cmp", () => {
|
||||
expect([8, 3, 5].sort(cmp())).toEqual([3, 5, 8]);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { identity, nop, partial, pipe } from "./composition.ts";
|
||||
import { expect, it } from "./deps.test.ts";
|
||||
import { expect, it } from "./deps.testing.ts";
|
||||
|
||||
it("nop", () => {
|
||||
expect(nop()).toBeUndefined();
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export * from "https://code.thunderk.net/typescript/devtools/raw/1.2.2/testing.ts";
|
5
deps.testing.ts
Normal file
5
deps.testing.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export {
|
||||
describe,
|
||||
expect,
|
||||
it,
|
||||
} from "https://js.thunderk.net/devtools@1.3.1/testing.ts";
|
3
doc/about.md
Normal file
3
doc/about.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
## About
|
||||
|
||||
Provides some common helpers for functional-style programming.
|
15
doc/import.md
Normal file
15
doc/import.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
## Import
|
||||
|
||||
In deno:
|
||||
|
||||
```typescript
|
||||
import { bool } from "https://js.thunderk.net/functional/mod.ts";
|
||||
```
|
||||
|
||||
In browser:
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
import { bool } from "https://js.thunderk.net/functional/mod.js";
|
||||
</script>
|
||||
```
|
3
doc/index
Normal file
3
doc/index
Normal file
|
@ -0,0 +1,3 @@
|
|||
about
|
||||
import
|
||||
use
|
157
doc/use.md
Normal file
157
doc/use.md
Normal file
|
@ -0,0 +1,157 @@
|
|||
## Use
|
||||
|
||||
**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]
|
||||
```
|
||||
|
||||
**defined** removes undefined values from an object:
|
||||
|
||||
```typescript
|
||||
defined({ a: 1, b: undefined }); // => {a: 1}
|
||||
```
|
||||
|
||||
**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);
|
||||
```
|
||||
|
||||
**options** merges options over default values:
|
||||
|
||||
```typescript
|
||||
options({ a: 1, b: 2, c: 3, d: 4 }, { a: 11, c: 33 }, { b: 22 });
|
||||
// => {a: 11, b: 22, c: 33, d: 4}
|
||||
```
|
||||
|
||||
**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,4 +1,4 @@
|
|||
import { expect, it } from "./deps.test.ts";
|
||||
import { expect, it } from "./deps.testing.ts";
|
||||
import { at, first, fmap, last, second, third } from "./iterables.ts";
|
||||
|
||||
it("at", () => {
|
||||
|
|
|
@ -1,8 +1,31 @@
|
|||
import { expect, it } from "./deps.test.ts";
|
||||
import { attr } from "./objects.ts";
|
||||
import { expect, it } from "./deps.testing.ts";
|
||||
import { attr, defined, options } from "./objects.ts";
|
||||
|
||||
it("attr", () => {
|
||||
const getx = attr("x");
|
||||
expect(getx({ x: 4, y: 5 })).toBe(4);
|
||||
expect(getx({ x: undefined, y: 5 })).toBeUndefined();
|
||||
});
|
||||
|
||||
it("defined", () => {
|
||||
expect(defined({})).toEqual({});
|
||||
expect(defined({ x: 1 })).toEqual({ x: 1 });
|
||||
expect(defined({ x: 1, y: undefined })).toEqual({ x: 1 });
|
||||
|
||||
const obj = defined({ x: 1, y: undefined });
|
||||
// @ts-expect-error
|
||||
expect(obj.y).toBeUndefined();
|
||||
});
|
||||
|
||||
it("options", () => {
|
||||
expect(options({})).toEqual({});
|
||||
expect(options({ x: 1, y: 2 }, { x: 3 })).toEqual({ x: 3, y: 2 });
|
||||
expect(options({ x: 1, y: 2 }, { x: 3 }, { x: 4 })).toEqual({ x: 4, y: 2 });
|
||||
expect(options({ x: 1, y: 2 }, { x: 3 }, { y: 0 })).toEqual({ x: 3, y: 0 });
|
||||
expect(options({ x: 1, y: 2 }, { x: undefined, y: 3 })).toEqual({
|
||||
x: 1,
|
||||
y: 3,
|
||||
});
|
||||
// @ts-expect-error
|
||||
expect(options({ x: 1 }, { y: 2 })).toEqual({ x: 1, y: 2 });
|
||||
});
|
||||
|
|
26
objects.ts
26
objects.ts
|
@ -1,8 +1,28 @@
|
|||
/**
|
||||
* Attribute getter
|
||||
*/
|
||||
// Attribute getter
|
||||
export function attr<K extends string>(
|
||||
name: K,
|
||||
): <O extends Record<K, O[K]>>(obj: O) => O[K] {
|
||||
return (obj) => obj[name];
|
||||
}
|
||||
|
||||
type DefinedKeys<T> =
|
||||
({ [P in keyof T]: T[P] extends undefined ? never : P })[keyof T];
|
||||
type Defined<T> = Pick<T, DefinedKeys<T>>;
|
||||
|
||||
// Removes undefined values from objects
|
||||
export function defined<T>(obj: T): Defined<T> {
|
||||
return Object.fromEntries(
|
||||
Object.entries(obj).filter(([_, value]) => typeof value !== "undefined"),
|
||||
) as any;
|
||||
}
|
||||
|
||||
// Apply options to a default object
|
||||
export function options<T extends { [name: string]: any }>(
|
||||
defaults: T,
|
||||
...options: Partial<T>[]
|
||||
) {
|
||||
for (let opts of options) {
|
||||
defaults = { ...defaults, ...defined(opts) };
|
||||
}
|
||||
return defaults;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { expect, it } from "./deps.test.ts";
|
||||
import { expect, it } from "./deps.testing.ts";
|
||||
import { always, and, never, not, or } from "./predicates.ts";
|
||||
|
||||
it("always", () => {
|
||||
|
|
19
run
Executable file
19
run
Executable file
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
# Simplified run tool for deno commands
|
||||
|
||||
if test $# -eq 0
|
||||
then
|
||||
echo "Usage: $0 [file or command]"
|
||||
exit 1
|
||||
elif echo $1 | grep -q '.*.ts'
|
||||
then
|
||||
denocmd=run
|
||||
denoargs=$1
|
||||
shift
|
||||
else
|
||||
denocmd=$1
|
||||
shift
|
||||
fi
|
||||
|
||||
denoargs="$(cat config/$denocmd.flags 2> /dev/null) $denoargs $@"
|
||||
exec deno $denocmd $denoargs
|
|
@ -1,4 +1,4 @@
|
|||
import { expect, it } from "./deps.test.ts";
|
||||
import { expect, it } from "./deps.testing.ts";
|
||||
import { isinstance, nn, nnu, nu } from "./typing.ts";
|
||||
|
||||
it("isinstance", () => {
|
||||
|
|
Loading…
Reference in a new issue