{"id":15429830,"url":"https://github.com/slmgc/nothing","last_synced_at":"2025-05-16T18:09:58.299Z","repository":{"id":28306559,"uuid":"92079585","full_name":"slmgc/Nothing","owner":"slmgc","description":"A chainable, callable mock object which always returns itself","archived":false,"fork":false,"pushed_at":"2023-10-17T03:00:28.000Z","size":568,"stargazers_count":363,"open_issues_count":2,"forks_count":7,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-03T17:13:36.483Z","etag":null,"topics":["callable","chainable","mock","nothing","testing"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/slmgc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-05-22T17:17:52.000Z","updated_at":"2024-07-21T18:50:38.000Z","dependencies_parsed_at":"2024-01-13T22:25:36.853Z","dependency_job_id":"b102e29c-6851-48c5-a1d4-cf12b223520a","html_url":"https://github.com/slmgc/Nothing","commit_stats":{"total_commits":21,"total_committers":4,"mean_commits":5.25,"dds":0.1428571428571429,"last_synced_commit":"fd8441054b11fc6e245fe0e023aae4956ea07956"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slmgc%2FNothing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slmgc%2FNothing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slmgc%2FNothing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slmgc%2FNothing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/slmgc","download_url":"https://codeload.github.com/slmgc/Nothing/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248601062,"owners_count":21131607,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["callable","chainable","mock","nothing","testing"],"created_at":"2024-10-01T18:13:05.191Z","updated_at":"2025-04-12T16:47:38.811Z","avatar_url":"https://github.com/slmgc.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Nothing\n\n[![npm package][npm-badge]][npm] [![npm package][npm-downloads]][npm]\n\n**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.\n\n## How to install\n\n```\nnpm i -S nothing-mock\n```\n\n## How to use\n\n### A simple example\n```js\nimport {Nothing} from 'nothing-mock'\n\nconst foo = Nothing\nfoo.bar.baz.qux().spam.ham[0].map((x) =\u003e x + 1).eggs.someFn() // returns Nothing\n```\n\n### Reducing boilerplate\n```js\nimport {Nothing} from 'nothing-mock'\n\n// A regular function with null-checks\nfunction someFunctionWithNullChecks(foo) {\n\treturn foo \u0026\u0026\n\t\tfoo.bar \u0026\u0026\n\t\tfoo.bar.baz \u0026\u0026\n\t\tfoo.bar.baz.qux \u0026\u0026\n\t\tfoo.bar.baz.qux()\n}\n\n// There is no need to check for null/undefined if you use Nothing\nfunction someFunction(foo) {\n\treturn foo.bar.baz.qux()\n}\n\nsomeFunctionWithNullChecks(null) // returns null\nsomeFunction(Nothing) // returns Nothing\nsomeFunction(null) // throws an exception\n```\n\n### JSON serialization/deserialization\n```js\nimport {Nothing, deserialize, serialize} from 'nothing-mock'\n\nconst json = `{\n\t\"posts\": [{\n\t\t\"id\": 1,\n\t\t\"userId\": 12,\n\t\t\"content\": \"post 1\",\n\t\t\"comments\": [{\n\t\t\t\"id\": 1,\n\t\t\t\"userId\": 34,\n\t\t\t\"content\": \"comment 1\"\n\t\t}, {\n\t\t\t\"id\": 2,\n\t\t\t\"userId\": 56,\n\t\t\t\"content\": \"comment 2\"\n\t\t}]\n\t}, {\n\t\t\"id\": 2,\n\t\t\"userId\": 78,\n\t\t\"content\": \"post 2\",\n\t\t\"comments\": null\n\t}]\n}`\n\nconst {posts} = deserialize(json) /* returns: [{\n\t\"id\": 1,\n\t\"userId\": 12,\n\t\"content\": \"post 1\",\n\t\"comments\": [{\n\t\t\"id\": 1,\n\t\t\"userId\": 34,\n\t\t\"content\": \"comment 1\"\n\t}, {\n\t\t\"id\": 2,\n\t\t\"userId\": 56,\n\t\t\"content\": \"comment 2\"\n\t}]\n}, {\n\t\"id\": 2,\n\t\"userId\": 78,\n\t\"content\": \"post 2\",\n\t\"comments\": Nothing // null values are replaced with Nothing\n}] */\n\nfunction renderPostWithComments(post) {\n\treturn `\u003cdiv\u003e\n\t\t\u003cp\u003e${post.content}\u003c/p\u003e\n\t\t\u003cul\u003e${post.comments.map((comment) =\u003e\n\t\t\t`\u003cli\u003e${comment.content}\u003c/li\u003e`).join('')\n\t\t}\u003c/ul\u003e\n\t\u003c/div\u003e`\n}\n\nposts.map(renderPostWithComments).join('') /* returns:\n`\u003cdiv\u003e\n\t\u003cp\u003epost 1\u003c/p\u003e\n\t\u003cul\u003e\n\t\t\u003cli\u003ecomment 1\u003c/li\u003e\n\t\t\u003cli\u003ecomment 2\u003c/li\u003e\n\t\u003c/ul\u003e\n\u003c/div\u003e\n\u003cdiv\u003e\n\t\u003cp\u003epost 2\u003c/p\u003e\n\t\u003cul\u003e\u003c/ul\u003e // Nothing is rendered empty\n\u003c/div\u003e` */\n\n// Serializes an object to JSON and\n// replaces all Nothing values with null\nserialize({posts})\n```\n\n### Helper functions\n```js\nimport {Nothing, toBool, isNothing, isSomething} from 'nothing-mock'\n\nconst list = [Nothing, true, false, null, undefined, 0, 1, NaN, '', {}, []]\nlist.filter(toBool) // [true, 1, {}, []]\nlist.filter(isNothing) // [Nothing]\nlist.filter(isSomething) // [true, false, 0, 1, NaN, \"\", {}, []]\n```\n\n### Properties which don't return Nothing\n```jsx\nimport {Nothing} from 'nothing-mock'\n\nNothing.length // 0\nNothing.name // a string\nNothing.prototype // an object with a constructor\nNothing.toLocaleString() // \"\"\nNothing.toString() // \"\"\nNothing.valueOf() // false\n```\n\n### Gotchas\n```js\nimport {Nothing, toBool} from 'nothing-mock'\n\nString(Nothing) // \"\"\nNothing.toString() // \"\"\nNothing + 'a string' // \"a string\"\nNothing * 123 // 0\nNothing - 123 // -123\n\n// Gotcha: concatenation of Nothing and a number returns a string\nNothing + 123 // \"123\"\n\n// Solution: Nothing can be excplicitly converted to a number\nNumber(Nothing) // 0\nNumber(Nothing) + 123 // 123\n\n// Gotcha: typecasting of Nothing to a boolean returns true\nBoolean(Nothing) // true\n!!Nothing // true\n\n// Solution: Nothing can be converted to false\nNothing.valueOf() // false\ntoBool(Nothing) // false\n\n// Gotcha: returning Nothing from a promise never\n// resolves as Nothing is a thenable object\nsomePromise\n\t.then(() =\u003e Nothing)\n\t.then((result) =\u003e result) // pending indefinitely\n\n// Solution: wrapping Nothing resolves the issue\nsomePromise\n\t.then(() =\u003e ({result: Nothing}))\n\t.then((result) =\u003e result) // promise resolves\n```\n\n## FAQ\n\nQ-1: Proxies are slow and there is a runtime overhead. Why should I use **Nothing**?\n\nA: You should keep a few things in mind:\n\n1. \"Premature optimization is the root of all evil\" - Donald E. Knuth.\n2. 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.\n3. You can use **Nothing** for writing unit tests which are less likely to be performance-dependant.\n\nQ-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.:\n\n```js\ntry {\n\tfoo.bar.baz()\n} catch (e) {\n\t// deal with it somehow\n}\n```\n\nA: 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.\n\nYou can always check the result if a function call should never return **Nothing** and then handle it properly:\n\n```js\nconst someFunction = (handleNothing, arg) =\u003e {\n\tconst result = foo.bar.baz(arg)\n\treturn isNothing(result) ? handleNothing(arg) : result\n}\n```\n\nQ-3: Why should I use **Nothing** if there are better alternatives like [optional chaining] or [lodash.get]?\n\nA: Each of these solutions have their pros and cons. Your choice should depend on the use-case:\n\n1. 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.\n2. `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:\n\n```js\nimport get from 'lodash.get'\n\nvar foo = null\nget(foo, ['bar', 'baz'])() // this will throw an exception\n\nvar baz = get(foo, ['bar', 'baz'])\nbaz \u0026\u0026 baz() // this won't work if `baz` should be bound to the context of `bar`\n\n// For example:\nvar foo = {\n\tbar: {\n\t\tbaz() {\n\t\t\tconsole.log(this.qux)\n\t\t},\n\t\tqux: 'hello'\n\t}\n}\n\nfoo.bar.baz() // \"hello\"\nget(foo, ['bar', 'baz'])() // undefined\n\n// This would be a proper solution:\nvar bar = get(foo, ['bar'])\nvar baz = get(bar, ['baz'])\nbaz \u0026\u0026 baz.call(bar) // \"hello\"\n\n// But then it's easier to get back to the regular syntax:\nfoo \u0026\u0026 foo.bar \u0026\u0026 foo.bar.baz \u0026\u0026 foo.bar.baz()\n\n// And good luck using `get` for something like this:\nfoo.bar.baz()[0].map(() =\u003e { /* do something */ })\n\n// BTW, an implementation of a lodash-like `get` helper-function is basically a one-liner:\nconst get = (o, a) =\u003e a.reduce((p, c) =\u003e p \u0026\u0026 p[c], o)\n```\n\nQ-4: I am still not convinced and ain't gonna use **Nothing**!\n\nA: Thanks for letting me know! Seriously, it's your choice, I am down with it.\n\n## License\n**MIT**\n\n[Proxy]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#Browser_compatibility\n[Symbol]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol#Browser_compatibility\n[npm]: https://www.npmjs.org/package/nothing-mock\n[npm-badge]: https://img.shields.io/npm/v/nothing-mock.svg\n[npm-downloads]: https://img.shields.io/npm/dm/nothing-mock.svg\n[optional chaining]: https://www.npmjs.com/package/babel-plugin-transform-optional-chaining\n[lodash.get]: https://www.npmjs.com/package/lodash.get\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslmgc%2Fnothing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fslmgc%2Fnothing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslmgc%2Fnothing/lists"}