Added and, or, pipe, attr, nn, nu, nnu, bool and fmap
This commit is contained in:
parent
280ec25df9
commit
515612f089
5 changed files with 407 additions and 60 deletions
84
README.md
84
README.md
|
@ -18,10 +18,62 @@ Issues can be reported on [GitLab](https://gitlab.com/thunderk/tk-functional/iss
|
|||
Functions
|
||||
---------
|
||||
|
||||
**nop** does nothing (useful for some callbacks):
|
||||
**always** and **never** return true and false respectively
|
||||
|
||||
```typescript
|
||||
new ConstructorWithMandatoryCallback(nop)
|
||||
always() // => true
|
||||
never() // => false
|
||||
```
|
||||
|
||||
**and** and **or** combines 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
|
||||
```
|
||||
|
||||
**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]
|
||||
```
|
||||
|
||||
**identity** returns its argument untouched:
|
||||
|
@ -30,11 +82,26 @@ new ConstructorWithMandatoryCallback(nop)
|
|||
a === identity(a) // => true
|
||||
```
|
||||
|
||||
**always** and **never** return true and false respectively
|
||||
**nn**, **nu** and **nnu** checks at run-time for null or undefined:
|
||||
|
||||
```typescript
|
||||
always() // => true
|
||||
never() // => false
|
||||
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:
|
||||
|
@ -45,10 +112,9 @@ const plus1 = partial({a: 1}, sum);
|
|||
plus1({b: 8}) // => 9
|
||||
```
|
||||
|
||||
**cmp** simplifies the use of array sorting:
|
||||
**pipe** chains two functions as one:
|
||||
|
||||
```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]
|
||||
const f = pipe((x: number) => x * 2, (x: number) => x + 1);
|
||||
f(3) // => 7 ((3 * 2) + 1)
|
||||
```
|
||||
|
|
123
package-lock.json
generated
123
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "tk-functional",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.5",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -1185,9 +1185,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "12.7.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.7.tgz",
|
||||
"integrity": "sha512-4jUncNe2tj1nmrO/34PsRpZqYVnRV1svbU78cKhuQKkMntKB/AmdLyGgswcZKjFHEHGpiY8pVD8CuVI55nP54w==",
|
||||
"version": "12.7.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.8.tgz",
|
||||
"integrity": "sha512-FMdVn84tJJdV+xe+53sYiZS4R5yn1mAIxfj+DVoNiQjTYz1+OYmjwEZr1ev9nU0axXwda0QDbYl06QHanRVH3A==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/q": {
|
||||
|
@ -1920,9 +1920,9 @@
|
|||
}
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
|
||||
"integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
|
||||
"version": "2.20.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.1.tgz",
|
||||
"integrity": "sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg==",
|
||||
"dev": true
|
||||
},
|
||||
"component-emitter": {
|
||||
|
@ -2549,9 +2549,9 @@
|
|||
}
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.266",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.266.tgz",
|
||||
"integrity": "sha512-UTuTZ4v8T0gLPHI7U75PXLQePWI65MTS3mckRrnLCkNljHvsutbYs+hn2Ua/RFul3Jt/L3Ht2rLP+dU/AlBfrQ==",
|
||||
"version": "1.3.269",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.269.tgz",
|
||||
"integrity": "sha512-t2ZTfo07HxkxTOUbIwMmqHBSnJsC9heqJUm7LwQu2iSk0wNhG4H5cMREtb8XxeCrQABDZ6IqQKY3yZq+NfAqwg==",
|
||||
"dev": true
|
||||
},
|
||||
"emoji-regex": {
|
||||
|
@ -2886,9 +2886,9 @@
|
|||
}
|
||||
},
|
||||
"filesize": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/filesize/-/filesize-4.2.0.tgz",
|
||||
"integrity": "sha512-bdS2UP98MZzLyTZzhuSH5ctAWyDt81n5xMti9BSdmgPXjjENLDz5Bmbk2R7ATVw/HRysZzWA2JIPgcSAOimWpw==",
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/filesize/-/filesize-4.2.1.tgz",
|
||||
"integrity": "sha512-bP82Hi8VRZX/TUBKfE24iiUGsB/sfm2WUrwTQyAzQrhO3V9IhcBBNBXMyzLY5orACxRyYJ3d2HeRVX+eFv4lmA==",
|
||||
"dev": true
|
||||
},
|
||||
"fill-range": {
|
||||
|
@ -3628,9 +3628,9 @@
|
|||
}
|
||||
},
|
||||
"handlebars": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.3.1.tgz",
|
||||
"integrity": "sha512-c0HoNHzDiHpBt4Kqe99N8tdLPKAnGCQ73gYMPWtAYM4PwGnf7xl8PBUHJqh9ijlzt2uQKaSRxbXRt+rZ7M2/kA==",
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.4.0.tgz",
|
||||
"integrity": "sha512-xkRtOt3/3DzTKMOt3xahj2M/EqNhY988T+imYSlMgs5fVhLN2fmKVVj0LtEGmb+3UUYV5Qmm1052Mm3dIQxOvw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"neo-async": "^2.6.0",
|
||||
|
@ -5034,6 +5034,12 @@
|
|||
"integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==",
|
||||
"dev": true
|
||||
},
|
||||
"memorystream": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
|
||||
"integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=",
|
||||
"dev": true
|
||||
},
|
||||
"merge-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||
|
@ -5262,9 +5268,9 @@
|
|||
}
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "1.1.32",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.32.tgz",
|
||||
"integrity": "sha512-VhVknkitq8dqtWoluagsGPn3dxTvN9fwgR59fV3D7sLBHe0JfDramsMI8n8mY//ccq/Kkrf8ZRHRpsyVZ3qw1A==",
|
||||
"version": "1.1.33",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.33.tgz",
|
||||
"integrity": "sha512-I0V30bWQEoHb+10W8oedVoUrdjW5wIkYm0w7vvcrPO95pZY738m1k77GF5sO0vKg5eXYg9oGtrMAETbgZGm11A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"semver": "^5.3.0"
|
||||
|
@ -5303,6 +5309,23 @@
|
|||
"integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==",
|
||||
"dev": true
|
||||
},
|
||||
"npm-run-all": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
|
||||
"integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"chalk": "^2.4.1",
|
||||
"cross-spawn": "^6.0.5",
|
||||
"memorystream": "^0.3.1",
|
||||
"minimatch": "^3.0.4",
|
||||
"pidtree": "^0.3.0",
|
||||
"read-pkg": "^3.0.0",
|
||||
"shell-quote": "^1.6.1",
|
||||
"string.prototype.padend": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"npm-run-path": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
|
||||
|
@ -5607,6 +5630,12 @@
|
|||
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
|
||||
"dev": true
|
||||
},
|
||||
"pidtree": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.0.tgz",
|
||||
"integrity": "sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg==",
|
||||
"dev": true
|
||||
},
|
||||
"pify": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
|
||||
|
@ -6349,9 +6378,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.9.0",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz",
|
||||
"integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==",
|
||||
"version": "16.10.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.10.1.tgz",
|
||||
"integrity": "sha512-BXUMf9sIOPXXZWqr7+c5SeOKJykyVr2u0UDzEf4LNGc6taGkQe1A9DFD07umCIXz45RLr9oAAwZbAJ0Pkknfaw==",
|
||||
"dev": true
|
||||
},
|
||||
"read-pkg": {
|
||||
|
@ -6621,14 +6650,14 @@
|
|||
}
|
||||
},
|
||||
"rollup": {
|
||||
"version": "1.21.4",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-1.21.4.tgz",
|
||||
"integrity": "sha512-Pl512XVCmVzgcBz5h/3Li4oTaoDcmpuFZ+kdhS/wLreALz//WuDAMfomD3QEYl84NkDu6Z6wV9twlcREb4qQsw==",
|
||||
"version": "1.22.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-1.22.0.tgz",
|
||||
"integrity": "sha512-x4l4ZrV/Mr/x/jvFTmwROdEAhbZjx16yDRTVSKWh/i4oJDuW2dVEbECT853mybYCz7BAitU8ElGlhx7dNjw3qQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/estree": "0.0.39",
|
||||
"@types/node": "^12.7.5",
|
||||
"acorn": "^7.0.0"
|
||||
"@types/estree": "*",
|
||||
"@types/node": "*",
|
||||
"acorn": "^7.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
|
@ -6640,9 +6669,9 @@
|
|||
}
|
||||
},
|
||||
"rollup-plugin-alias": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup-plugin-alias/-/rollup-plugin-alias-2.0.0.tgz",
|
||||
"integrity": "sha512-JVwxV9nwzjc0q7JmrV9jvZlJ8FykNd5r7RmYps8i/7v+vcmmVpeMvXrljhCboYErf4VZ2z9FEj+fP7eJlEGfug==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup-plugin-alias/-/rollup-plugin-alias-2.0.1.tgz",
|
||||
"integrity": "sha512-/wOySYmFzR1TPijsiNBOAV12HYPs7GZtAuim2LKdk5V6RnQSZ34ueVgkFfjJffe/HIFR6Jdwc4+7YOlY5uty4w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"slash": "^3.0.0"
|
||||
|
@ -6964,6 +6993,12 @@
|
|||
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
|
||||
"dev": true
|
||||
},
|
||||
"shell-quote": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz",
|
||||
"integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==",
|
||||
"dev": true
|
||||
},
|
||||
"shellwords": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
|
||||
|
@ -7306,6 +7341,17 @@
|
|||
"strip-ansi": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"string.prototype.padend": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz",
|
||||
"integrity": "sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"define-properties": "^1.1.2",
|
||||
"es-abstract": "^1.4.3",
|
||||
"function-bind": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"string.prototype.trimleft": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz",
|
||||
|
@ -7414,9 +7460,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"terser": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-4.3.2.tgz",
|
||||
"integrity": "sha512-obxk4x19Zlzj9zY4QeXj9iPCb5W8YGn4v3pn4/fHj0Nw8+R7N02Kvwvz9VpOItCZZD8RC+vnYCDL0gP6FAJ7Xg==",
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-4.3.4.tgz",
|
||||
"integrity": "sha512-Kcrn3RiW8NtHBP0ssOAzwa2MsIRQ8lJWiBG/K7JgqPlomA3mtb2DEmp4/hrUA+Jujx+WZ02zqd7GYD+QRBB/2Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commander": "^2.20.0",
|
||||
|
@ -7459,15 +7505,16 @@
|
|||
}
|
||||
},
|
||||
"tk-base": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tk-base/-/tk-base-0.2.0.tgz",
|
||||
"integrity": "sha512-ZdXXxHxjByinZy1ZOo26WOjqwkDkQBAFr0U0pKHMtw7tyJyTvMrxxaf/gm6IoaJLcMQ+vdwqFP5t0nmVyhTrog==",
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tk-base/-/tk-base-0.2.1.tgz",
|
||||
"integrity": "sha512-oVFVHXYoknqLBSZRqJSg/+BrKAh24cMpXQyU/q0hH+dVPnd+uHQ1nsjE8U69phrYOSCWicVXH1CKfuaGtV0vWg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/jest": "^24.0.18",
|
||||
"@types/node": "^12.7.7",
|
||||
"@types/node": "^12.7.8",
|
||||
"jest": "^24.9.0",
|
||||
"microbundle": "^0.12.0-next.6",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"ts-jest": "^24.1.0",
|
||||
"ts-node": "^8.4.1",
|
||||
"typescript": "^3.6.3"
|
||||
|
|
26
package.json
26
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "tk-functional",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.5",
|
||||
"description": "Typescript/Javascript helpers for functional-style programming",
|
||||
"source": "src/index.ts",
|
||||
"main": "dist/tk-functional.umd.js",
|
||||
|
@ -11,19 +11,33 @@
|
|||
"/dist"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "npx --no-install tk-base test",
|
||||
"normalize": "npx --no-install tk-base normalize",
|
||||
"build": "npx --no-install tk-base build",
|
||||
"test": "jest",
|
||||
"normalize": "tk-base",
|
||||
"build": "microbundle build -f modern,umd",
|
||||
"prepare": "npm run build",
|
||||
"prepublishOnly": "npm test"
|
||||
"prepublishOnly": "npm test",
|
||||
"dev": "run-p dev:*",
|
||||
"dev:test": "jest --watchAll",
|
||||
"dev:build": "microbundle watch -f modern,umd"
|
||||
},
|
||||
"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.0"
|
||||
"tk-base": "^0.2.1"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
|
@ -1,19 +1,19 @@
|
|||
import { always, cmp, identity, never, nop, partial } from "./index";
|
||||
import { always, and, attr, bool, cmp, fmap, identity, never, nn, nnu, nop, nu, or, partial, pipe } from "./index";
|
||||
|
||||
describe("nop", () => {
|
||||
describe(nop, () => {
|
||||
it("does nothing", () => {
|
||||
expect(nop()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("identity", () => {
|
||||
describe(identity, () => {
|
||||
it("returns the argument untouched", () => {
|
||||
expect(identity(5)).toBe(5);
|
||||
expect(identity(null)).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("partial", () => {
|
||||
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);
|
||||
|
@ -27,7 +27,7 @@ describe("partial", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("cmp", () => {
|
||||
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]);
|
||||
|
@ -36,7 +36,7 @@ describe("cmp", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("always", () => {
|
||||
describe(always, () => {
|
||||
it("returns true", () => {
|
||||
expect(always()).toBe(true);
|
||||
expect(always(true)).toBe(true);
|
||||
|
@ -44,10 +44,123 @@ describe("always", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("never", () => {
|
||||
describe(never, () => {
|
||||
it("returns false", () => {
|
||||
expect(never()).toBe(false);
|
||||
expect(never(true)).toBe(false);
|
||||
expect(never(false)).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(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(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]);
|
||||
});
|
||||
});
|
||||
|
|
107
src/index.ts
107
src/index.ts
|
@ -47,3 +47,110 @@ export const always = (...args: any[]) => true
|
|||
* Predicate that always returns false
|
||||
*/
|
||||
export const never = (...args: any[]) => false
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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): <V, O extends Record<K, V>>(obj: O) => V {
|
||||
return obj => obj[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue