├── .gitignore ├── LICENSE ├── README.md ├── dist ├── nothing-mock.es.js ├── nothing-mock.es.js.map ├── nothing-mock.js ├── nothing-mock.js.map ├── nothing-mock.modern.js ├── nothing-mock.modern.js.map ├── nothing-mock.umd.js └── nothing-mock.umd.js.map ├── index.d.ts ├── package-lock.json ├── package.json └── src └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.log* 2 | .DS_Store 3 | node_modules/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-present Vladimir Simonov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nothing 2 | 3 | [![npm package][npm-badge]][npm] [![npm package][npm-downloads]][npm] 4 | 5 | **Nothing** is a chainable, callable mock object which always returns itself. You can use it instead of `null` and `undefined` values so you don't have to place safety checks all over your code. The implementation uses [Symbol] and [Proxy] behind the hood which are widely supported by modern desktop and mobile browsers and can be used without a polyfill. 6 | 7 | ## How to install 8 | 9 | ``` 10 | npm i -S nothing-mock 11 | ``` 12 | 13 | ## How to use 14 | 15 | ### A simple example 16 | ```js 17 | import {Nothing} from 'nothing-mock' 18 | 19 | const foo = Nothing 20 | foo.bar.baz.qux().spam.ham[0].map((x) => x + 1).eggs.someFn() // returns Nothing 21 | ``` 22 | 23 | ### Reducing boilerplate 24 | ```js 25 | import {Nothing} from 'nothing-mock' 26 | 27 | // A regular function with null-checks 28 | function someFunctionWithNullChecks(foo) { 29 | return foo && 30 | foo.bar && 31 | foo.bar.baz && 32 | foo.bar.baz.qux && 33 | foo.bar.baz.qux() 34 | } 35 | 36 | // There is no need to check for null/undefined if you use Nothing 37 | function someFunction(foo) { 38 | return foo.bar.baz.qux() 39 | } 40 | 41 | someFunctionWithNullChecks(null) // returns null 42 | someFunction(Nothing) // returns Nothing 43 | someFunction(null) // throws an exception 44 | ``` 45 | 46 | ### JSON serialization/deserialization 47 | ```js 48 | import {Nothing, deserialize, serialize} from 'nothing-mock' 49 | 50 | const json = `{ 51 | "posts": [{ 52 | "id": 1, 53 | "userId": 12, 54 | "content": "post 1", 55 | "comments": [{ 56 | "id": 1, 57 | "userId": 34, 58 | "content": "comment 1" 59 | }, { 60 | "id": 2, 61 | "userId": 56, 62 | "content": "comment 2" 63 | }] 64 | }, { 65 | "id": 2, 66 | "userId": 78, 67 | "content": "post 2", 68 | "comments": null 69 | }] 70 | }` 71 | 72 | const {posts} = deserialize(json) /* returns: [{ 73 | "id": 1, 74 | "userId": 12, 75 | "content": "post 1", 76 | "comments": [{ 77 | "id": 1, 78 | "userId": 34, 79 | "content": "comment 1" 80 | }, { 81 | "id": 2, 82 | "userId": 56, 83 | "content": "comment 2" 84 | }] 85 | }, { 86 | "id": 2, 87 | "userId": 78, 88 | "content": "post 2", 89 | "comments": Nothing // null values are replaced with Nothing 90 | }] */ 91 | 92 | function renderPostWithComments(post) { 93 | return `
94 |

${post.content}

95 | 98 |
` 99 | } 100 | 101 | posts.map(renderPostWithComments).join('') /* returns: 102 | `
103 |

post 1

104 | 108 |
109 |
110 |

post 2

111 | // Nothing is rendered empty 112 |
` */ 113 | 114 | // Serializes an object to JSON and 115 | // replaces all Nothing values with null 116 | serialize({posts}) 117 | ``` 118 | 119 | ### Helper functions 120 | ```js 121 | import {Nothing, toBool, isNothing, isSomething} from 'nothing-mock' 122 | 123 | const list = [Nothing, true, false, null, undefined, 0, 1, NaN, '', {}, []] 124 | list.filter(toBool) // [true, 1, {}, []] 125 | list.filter(isNothing) // [Nothing] 126 | list.filter(isSomething) // [true, false, 0, 1, NaN, "", {}, []] 127 | ``` 128 | 129 | ### Properties which don't return Nothing 130 | ```jsx 131 | import {Nothing} from 'nothing-mock' 132 | 133 | Nothing.length // 0 134 | Nothing.name // a string 135 | Nothing.prototype // an object with a constructor 136 | Nothing.toLocaleString() // "" 137 | Nothing.toString() // "" 138 | Nothing.valueOf() // false 139 | ``` 140 | 141 | ### Gotchas 142 | ```js 143 | import {Nothing, toBool} from 'nothing-mock' 144 | 145 | String(Nothing) // "" 146 | Nothing.toString() // "" 147 | Nothing + 'a string' // "a string" 148 | Nothing * 123 // 0 149 | Nothing - 123 // -123 150 | 151 | // Gotcha: concatenation of Nothing and a number returns a string 152 | Nothing + 123 // "123" 153 | 154 | // Solution: Nothing can be excplicitly converted to a number 155 | Number(Nothing) // 0 156 | Number(Nothing) + 123 // 123 157 | 158 | // Gotcha: typecasting of Nothing to a boolean returns true 159 | Boolean(Nothing) // true 160 | !!Nothing // true 161 | 162 | // Solution: Nothing can be converted to false 163 | Nothing.valueOf() // false 164 | toBool(Nothing) // false 165 | 166 | // Gotcha: returning Nothing from a promise never 167 | // resolves as Nothing is a thenable object 168 | somePromise 169 | .then(() => Nothing) 170 | .then((result) => result) // pending indefinitely 171 | 172 | // Solution: wrapping Nothing resolves the issue 173 | somePromise 174 | .then(() => ({result: Nothing})) 175 | .then((result) => result) // promise resolves 176 | ``` 177 | 178 | ## FAQ 179 | 180 | Q-1: Proxies are slow and there is a runtime overhead. Why should I use **Nothing**? 181 | 182 | A: You should keep a few things in mind: 183 | 184 | 1. "Premature optimization is the root of all evil" - Donald E. Knuth. 185 | 2. Have you checked the performance of **Nothing**? Does it really impact the performance of your code? If it does, you can always opt out using **Nothing** for performance-critical parts of your code. 186 | 3. You can use **Nothing** for writing unit tests which are less likely to be performance-dependant. 187 | 188 | Q-2: I believe that it's hard to understand the logic as the code will fail silently if I would use **Nothing**. I prefer to use try/catch blocks instead, e.g.: 189 | 190 | ```js 191 | try { 192 | foo.bar.baz() 193 | } catch (e) { 194 | // deal with it somehow 195 | } 196 | ``` 197 | 198 | A: Many functional programming languages either don't have or don't endorse the use of imperative constructs such as try/catch blocks because they introduce so-called side effects which actually make it harder to debug and reason about the code. And programs which are written in functional programming languages are considered to be less error-prone and easier to support. 199 | 200 | You can always check the result if a function call should never return **Nothing** and then handle it properly: 201 | 202 | ```js 203 | const someFunction = (handleNothing, arg) => { 204 | const result = foo.bar.baz(arg) 205 | return isNothing(result) ? handleNothing(arg) : result 206 | } 207 | ``` 208 | 209 | Q-3: Why should I use **Nothing** if there are better alternatives like [optional chaining] or [lodash.get]? 210 | 211 | A: Each of these solutions have their pros and cons. Your choice should depend on the use-case: 212 | 213 | 1. Optional chaining syntax would be the best choice, but it requires a transpilation step as modern browsers don't support the syntax and it might take a while before it will get into the future ECMAScript standard. 214 | 2. `lodash.get` is good for a basic property chain traversal, but it requires an alien syntax and fails when there is a need to call a method somewhere in a property chain: 215 | 216 | ```js 217 | import get from 'lodash.get' 218 | 219 | var foo = null 220 | get(foo, ['bar', 'baz'])() // this will throw an exception 221 | 222 | var baz = get(foo, ['bar', 'baz']) 223 | baz && baz() // this won't work if `baz` should be bound to the context of `bar` 224 | 225 | // For example: 226 | var foo = { 227 | bar: { 228 | baz() { 229 | console.log(this.qux) 230 | }, 231 | qux: 'hello' 232 | } 233 | } 234 | 235 | foo.bar.baz() // "hello" 236 | get(foo, ['bar', 'baz'])() // undefined 237 | 238 | // This would be a proper solution: 239 | var bar = get(foo, ['bar']) 240 | var baz = get(bar, ['baz']) 241 | baz && baz.call(bar) // "hello" 242 | 243 | // But then it's easier to get back to the regular syntax: 244 | foo && foo.bar && foo.bar.baz && foo.bar.baz() 245 | 246 | // And good luck using `get` for something like this: 247 | foo.bar.baz()[0].map(() => { /* do something */ }) 248 | 249 | // BTW, an implementation of a lodash-like `get` helper-function is basically a one-liner: 250 | const get = (o, a) => a.reduce((p, c) => p && p[c], o) 251 | ``` 252 | 253 | Q-4: I am still not convinced and ain't gonna use **Nothing**! 254 | 255 | A: Thanks for letting me know! Seriously, it's your choice, I am down with it. 256 | 257 | ## License 258 | **MIT** 259 | 260 | [Proxy]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#Browser_compatibility 261 | [Symbol]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol#Browser_compatibility 262 | [npm]: https://www.npmjs.org/package/nothing-mock 263 | [npm-badge]: https://img.shields.io/npm/v/nothing-mock.svg 264 | [npm-downloads]: https://img.shields.io/npm/dm/nothing-mock.svg 265 | [optional chaining]: https://www.npmjs.com/package/babel-plugin-transform-optional-chaining 266 | [lodash.get]: https://www.npmjs.com/package/lodash.get 267 | -------------------------------------------------------------------------------- /dist/nothing-mock.es.js: -------------------------------------------------------------------------------- 1 | var n,r=((n=function(){return r}).toString=n.toLocaleString=n[Symbol.toPrimitive]=function(){return""},n.valueOf=function(){return!1},new Proxy(Object.freeze(n),{get:function(n,t){return n.hasOwnProperty(t)?n[t]:r}})),t=function(n){return!(!n||!n.valueOf())},u=function(n){return n===r},e=function(n){return!(n===r||null==n)},o=function(n){return JSON.stringify(n,function(n,t){return t===r?null:t})},i=function(n){return JSON.parse(n,function(n,t){return null===t?r:t})};export{r as Nothing,i as deserialize,u as isNothing,e as isSomething,o as serialize,t as toBool}; 2 | //# sourceMappingURL=nothing-mock.es.js.map 3 | -------------------------------------------------------------------------------- /dist/nothing-mock.es.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"nothing-mock.es.js","sources":["../src/index.js"],"sourcesContent":["export const Nothing = (() => {\n\tconst fn = () => Nothing\n\tfn.toString = fn.toLocaleString = fn[Symbol.toPrimitive] = () => ''\n\tfn.valueOf = () => false\n\n\treturn new Proxy(Object.freeze(fn), {\n\t\tget: (o, key) => o.hasOwnProperty(key) ? o[key] : Nothing\n\t})\n})()\n\nexport const toBool = (o) => !!(o && o.valueOf())\nexport const isNothing = (o) => o === Nothing\nexport const isSomething = (o) => !(o === Nothing || o == null)\nexport const serialize = (o) => JSON.stringify(o, (k, v) => v === Nothing ? null : v)\nexport const deserialize = (s) => JSON.parse(s, (k, v) => v === null ? Nothing : v)"],"names":["Nothing","fn","toString","toLocaleString","Symbol","toPrimitive","valueOf","Proxy","Object","freeze","get","o","key","hasOwnProperty","toBool","isNothing","isSomething","serialize","JSON","stringify","k","v","deserialize","s","parse"],"mappings":"AAAaA,IACNC,EADMD,IACNC,EAAK,kBAAMD,IACdE,SAAWD,EAAGE,eAAiBF,EAAGG,OAAOC,aAAe,iBAAM,IACjEJ,EAAGK,QAAU,yBAEFC,MAAMC,OAAOC,OAAOR,GAAK,CACnCS,IAAK,SAACC,EAAGC,UAAQD,EAAEE,eAAeD,GAAOD,EAAEC,GAAOZ,MAIvCc,EAAS,SAACH,YAASA,IAAKA,EAAEL,YAC1BS,EAAY,SAACJ,UAAMA,IAAMX,GACzBgB,EAAc,SAACL,WAAQA,IAAMX,GAAgB,MAALW,IACxCM,EAAY,SAACN,UAAMO,KAAKC,UAAUR,EAAG,SAACS,EAAGC,UAAMA,IAAMrB,EAAU,KAAOqB,KACtEC,EAAc,SAACC,UAAML,KAAKM,MAAMD,EAAG,SAACH,EAAGC,UAAY,OAANA,EAAarB,EAAUqB"} -------------------------------------------------------------------------------- /dist/nothing-mock.js: -------------------------------------------------------------------------------- 1 | "use strict";var n,t=((n=function(){return t}).toString=n.toLocaleString=n[Symbol.toPrimitive]=function(){return""},n.valueOf=function(){return!1},new Proxy(Object.freeze(n),{get:function(n,r){return n.hasOwnProperty(r)?n[r]:t}}));exports.Nothing=t,exports.deserialize=function(n){return JSON.parse(n,function(n,r){return null===r?t:r})},exports.isNothing=function(n){return n===t},exports.isSomething=function(n){return!(n===t||null==n)},exports.serialize=function(n){return JSON.stringify(n,function(n,r){return r===t?null:r})},exports.toBool=function(n){return!(!n||!n.valueOf())}; 2 | //# sourceMappingURL=nothing-mock.js.map 3 | -------------------------------------------------------------------------------- /dist/nothing-mock.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"nothing-mock.js","sources":["../src/index.js"],"sourcesContent":["export const Nothing = (() => {\n\tconst fn = () => Nothing\n\tfn.toString = fn.toLocaleString = fn[Symbol.toPrimitive] = () => ''\n\tfn.valueOf = () => false\n\n\treturn new Proxy(Object.freeze(fn), {\n\t\tget: (o, key) => o.hasOwnProperty(key) ? o[key] : Nothing\n\t})\n})()\n\nexport const toBool = (o) => !!(o && o.valueOf())\nexport const isNothing = (o) => o === Nothing\nexport const isSomething = (o) => !(o === Nothing || o == null)\nexport const serialize = (o) => JSON.stringify(o, (k, v) => v === Nothing ? null : v)\nexport const deserialize = (s) => JSON.parse(s, (k, v) => v === null ? Nothing : v)"],"names":["Nothing","fn","toString","toLocaleString","Symbol","toPrimitive","valueOf","Proxy","Object","freeze","get","o","key","hasOwnProperty","s","JSON","parse","k","v","stringify"],"mappings":"aAAaA,IACNC,EADMD,IACNC,EAAK,kBAAMD,IACdE,SAAWD,EAAGE,eAAiBF,EAAGG,OAAOC,aAAe,iBAAM,IACjEJ,EAAGK,QAAU,yBAEFC,MAAMC,OAAOC,OAAOR,GAAK,CACnCS,IAAK,SAACC,EAAGC,UAAQD,EAAEE,eAAeD,GAAOD,EAAEC,GAAOZ,4CAQzB,SAACc,UAAMC,KAAKC,MAAMF,EAAG,SAACG,EAAGC,UAAY,OAANA,EAAalB,EAAUkB,uBAHxD,SAACP,UAAMA,IAAMX,uBACX,SAACW,WAAQA,IAAMX,GAAgB,MAALW,sBAC5B,SAACA,UAAMI,KAAKI,UAAUR,EAAG,SAACM,EAAGC,UAAMA,IAAMlB,EAAU,KAAOkB,oBAH7D,SAACP,YAASA,IAAKA,EAAEL"} -------------------------------------------------------------------------------- /dist/nothing-mock.modern.js: -------------------------------------------------------------------------------- 1 | const e=(()=>{const t=()=>e;return t.toString=t.toLocaleString=t[Symbol.toPrimitive]=()=>"",t.valueOf=()=>!1,new Proxy(Object.freeze(t),{get:(t,r)=>t.hasOwnProperty(r)?t[r]:e})})(),t=e=>!(!e||!e.valueOf()),r=t=>t===e,n=t=>!(t===e||null==t),l=t=>JSON.stringify(t,(t,r)=>r===e?null:r),o=t=>JSON.parse(t,(t,r)=>null===r?e:r);export{e as Nothing,o as deserialize,r as isNothing,n as isSomething,l as serialize,t as toBool}; 2 | //# sourceMappingURL=nothing-mock.modern.js.map 3 | -------------------------------------------------------------------------------- /dist/nothing-mock.modern.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"nothing-mock.modern.js","sources":["../src/index.js"],"sourcesContent":["export const Nothing = (() => {\n\tconst fn = () => Nothing\n\tfn.toString = fn.toLocaleString = fn[Symbol.toPrimitive] = () => ''\n\tfn.valueOf = () => false\n\n\treturn new Proxy(Object.freeze(fn), {\n\t\tget: (o, key) => o.hasOwnProperty(key) ? o[key] : Nothing\n\t})\n})()\n\nexport const toBool = (o) => !!(o && o.valueOf())\nexport const isNothing = (o) => o === Nothing\nexport const isSomething = (o) => !(o === Nothing || o == null)\nexport const serialize = (o) => JSON.stringify(o, (k, v) => v === Nothing ? null : v)\nexport const deserialize = (s) => JSON.parse(s, (k, v) => v === null ? Nothing : v)"],"names":["Nothing","fn","toString","toLocaleString","Symbol","toPrimitive","valueOf","Proxy","Object","freeze","get","o","key","hasOwnProperty","toBool","isNothing","isSomething","serialize","JSON","stringify","k","v","deserialize","s","parse"],"mappings":"AAAaA,MAAAA,EAAU,MACtB,MAAMC,EAAK,IAAMD,EAIjB,OAHAC,EAAGC,SAAWD,EAAGE,eAAiBF,EAAGG,OAAOC,aAAe,IAAM,GACjEJ,EAAGK,QAAU,KAAM,MAERC,MAAMC,OAAOC,OAAOR,GAAK,CACnCS,IAAK,CAACC,EAAGC,IAAQD,EAAEE,eAAeD,GAAOD,EAAEC,GAAOZ,KAN7B,GAUVc,EAAUH,MAASA,IAAKA,EAAEL,WAC1BS,EAAaJ,GAAMA,IAAMX,EACzBgB,EAAeL,KAAQA,IAAMX,GAAgB,MAALW,GACxCM,EAAaN,GAAMO,KAAKC,UAAUR,EAAG,CAACS,EAAGC,IAAMA,IAAMrB,EAAU,KAAOqB,GACtEC,EAAeC,GAAML,KAAKM,MAAMD,EAAG,CAACH,EAAGC,IAAY,OAANA,EAAarB,EAAUqB"} -------------------------------------------------------------------------------- /dist/nothing-mock.umd.js: -------------------------------------------------------------------------------- 1 | !function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((n||self).nothingMock={})}(this,function(n){"use strict";var t,e=((t=function(){return e}).toString=t.toLocaleString=t[Symbol.toPrimitive]=function(){return""},t.valueOf=function(){return!1},new Proxy(Object.freeze(t),{get:function(n,t){return n.hasOwnProperty(t)?n[t]:e}}));n.Nothing=e,n.deserialize=function(n){return JSON.parse(n,function(n,t){return null===t?e:t})},n.isNothing=function(n){return n===e},n.isSomething=function(n){return!(n===e||null==n)},n.serialize=function(n){return JSON.stringify(n,function(n,t){return t===e?null:t})},n.toBool=function(n){return!(!n||!n.valueOf())}}); 2 | //# sourceMappingURL=nothing-mock.umd.js.map 3 | -------------------------------------------------------------------------------- /dist/nothing-mock.umd.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"nothing-mock.umd.js","sources":["../src/index.js"],"sourcesContent":["export const Nothing = (() => {\n\tconst fn = () => Nothing\n\tfn.toString = fn.toLocaleString = fn[Symbol.toPrimitive] = () => ''\n\tfn.valueOf = () => false\n\n\treturn new Proxy(Object.freeze(fn), {\n\t\tget: (o, key) => o.hasOwnProperty(key) ? o[key] : Nothing\n\t})\n})()\n\nexport const toBool = (o) => !!(o && o.valueOf())\nexport const isNothing = (o) => o === Nothing\nexport const isSomething = (o) => !(o === Nothing || o == null)\nexport const serialize = (o) => JSON.stringify(o, (k, v) => v === Nothing ? null : v)\nexport const deserialize = (s) => JSON.parse(s, (k, v) => v === null ? Nothing : v)"],"names":["fn","Nothing","toString","toLocaleString","Symbol","toPrimitive","valueOf","Proxy","Object","freeze","get","o","key","hasOwnProperty","s","JSON","parse","k","v","stringify"],"mappings":"sPACOA,EADMC,IACND,EAAK,kBAAMC,IACdC,SAAWF,EAAGG,eAAiBH,EAAGI,OAAOC,aAAe,iBAAM,IACjEL,EAAGM,QAAU,yBAEFC,MAAMC,OAAOC,OAAOT,GAAK,CACnCU,IAAK,SAACC,EAAGC,UAAQD,EAAEE,eAAeD,GAAOD,EAAEC,GAAOX,gCAQzB,SAACa,UAAMC,KAAKC,MAAMF,EAAG,SAACG,EAAGC,UAAY,OAANA,EAAajB,EAAUiB,iBAHxD,SAACP,UAAMA,IAAMV,iBACX,SAACU,WAAQA,IAAMV,GAAgB,MAALU,gBAC5B,SAACA,UAAMI,KAAKI,UAAUR,EAAG,SAACM,EAAGC,UAAMA,IAAMjB,EAAU,KAAOiB,cAH7D,SAACP,YAASA,IAAKA,EAAEL"} -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | export const Nothing: any; 2 | export function toBool(o: any): boolean; 3 | export function isNothing(o: any): boolean; 4 | export function isSomething(o: any): boolean; 5 | export function serialize(o: any): string; 6 | export function deserialize(s: any): any; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nothing-mock", 3 | "version": "1.0.4", 4 | "license": "MIT", 5 | "description": "A chainable, callable mock object which always returns itself", 6 | "author": "Vladimir Simonov ", 7 | "homepage": "https://github.com/slmgc/Nothing", 8 | "repository": "github:slmgc/Nothing", 9 | "main": "dist/nothing-mock.js", 10 | "module": "dist/nothing-mock.es.js", 11 | "esmodule": "dist/nothing-mock.modern.js", 12 | "jsnext:main": "dist/nothing-mock.es.js", 13 | "unpkg": "dist/nothing-mock.umd.js", 14 | "source": "src/index.js", 15 | "types": "index.d.ts", 16 | "scripts": { 17 | "build": "microbundle --strict" 18 | }, 19 | "devDependencies": { 20 | "microbundle": "^0.13.0" 21 | }, 22 | "keywords": [ 23 | "nothing", 24 | "chainable", 25 | "callable", 26 | "mock", 27 | "testing" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export const Nothing = (() => { 2 | const fn = () => Nothing 3 | fn.toString = fn.toLocaleString = fn[Symbol.toPrimitive] = () => '' 4 | fn.valueOf = () => false 5 | 6 | return new Proxy(Object.freeze(fn), { 7 | get: (o, key) => o.hasOwnProperty(key) ? o[key] : Nothing 8 | }) 9 | })() 10 | 11 | export const toBool = (o) => !!(o && o.valueOf()) 12 | export const isNothing = (o) => o === Nothing 13 | export const isSomething = (o) => !(o === Nothing || o == null) 14 | export const serialize = (o) => JSON.stringify(o, (k, v) => v === Nothing ? null : v) 15 | export const deserialize = (s) => JSON.parse(s, (k, v) => v === null ? Nothing : v) --------------------------------------------------------------------------------