{"id":29079514,"url":"https://github.com/wherejuly/68-null-object","last_synced_at":"2025-06-27T17:08:10.462Z","repository":{"id":299762304,"uuid":"1004097268","full_name":"WhereJuly/68-null-object","owner":"WhereJuly","description":"The tiny polymorphic noop-behavior object to stub actual objects conditionally.","archived":false,"fork":false,"pushed_at":"2025-06-19T04:39:28.000Z","size":233,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"develop","last_synced_at":"2025-06-27T17:06:14.180Z","etag":null,"topics":["chainable-noop","noop-behavior","noop-function","noop-method","noop-object","null-object","polymorphic","stub-property","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@wherejuly/null-object","language":"TypeScript","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/WhereJuly.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,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-06-18T05:46:37.000Z","updated_at":"2025-06-19T04:39:32.000Z","dependencies_parsed_at":"2025-06-18T07:28:33.399Z","dependency_job_id":null,"html_url":"https://github.com/WhereJuly/68-null-object","commit_stats":null,"previous_names":["wherejuly/68-null-object"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/WhereJuly/68-null-object","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WhereJuly%2F68-null-object","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WhereJuly%2F68-null-object/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WhereJuly%2F68-null-object/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WhereJuly%2F68-null-object/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/WhereJuly","download_url":"https://codeload.github.com/WhereJuly/68-null-object/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WhereJuly%2F68-null-object/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262298768,"owners_count":23289603,"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":["chainable-noop","noop-behavior","noop-function","noop-method","noop-object","null-object","polymorphic","stub-property","typescript"],"created_at":"2025-06-27T17:06:10.814Z","updated_at":"2025-06-27T17:08:10.427Z","avatar_url":"https://github.com/WhereJuly.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Null-Object\n\nA tiny runtime type-safe _polymorphic noop object_ that substitutes for your real implementation without side effects.\n\n\u003csmall\u003eSee Null Object pattern [^1].\u003c/small\u003e\n\n[^1]: [Kerievsky J., Refactoring to Patterns (2004)](https://www.amazon.com/Refactoring-Patterns-Joshua-Kerievsky/dp/0321213351), p. 343; [Fowler M., Refactoring (2018)](https://www.amazon.com/Refactoring-Improving-Existing-Addison-Wesley-Signature/dp/0134757599), p. 289 under \"Introduce Special Case\" title.\n\n**Package Status**\n\n![Codecov](https://img.shields.io/codecov/c/github/WhereJuly/68-null-object?color=%2308A108)\n[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=WhereJuly_68-null-object\u0026metric=alert_status)](https://sonarcloud.io/summary/new_code?id=WhereJuly_68-null-object)\n[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=WhereJuly_68-null-object\u0026metric=sqale_index)](https://sonarcloud.io/summary/new_code?id=WhereJuly_68-null-object)\n[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=WhereJuly_68-null-object\u0026metric=code_smells)](https://sonarcloud.io/summary/new_code?id=WhereJuly_68-null-object)\n[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=WhereJuly_68-null-object\u0026metric=security_rating)](https://sonarcloud.io/summary/new_code?id=WhereJuly_68-null-object)\n\n![npm bundle size](https://img.shields.io/bundlephobia/min/@wherejuly/null-object)\n![npm bundle size](https://img.shields.io/bundlephobia/minzip/@wherejuly/null-object)\n![npm downloads](https://img.shields.io/npm/dm/env-schema-cli.svg?color=green)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?color=green)](https://opensource.org/licenses/MIT)\n\n---\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e\u003ca\u003eContents\u003c/a\u003e\u003c/b\u003e\u003c/summary\u003e\n\n- [Basic Usage](#basic-usage)\n- [Summary](#summary)\n- [Usage](#usage)\n  - [The Convenience noop Function](#the-convenience-noop-function)\n- [Maintenance](#maintenance)\n  - [Contributions](#contributions)\n- [License](#license)\n\n\u003c/details\u003e\n\n## Basic Usage\n\nInstall the package.\n\n```bash\nnpm install @wherejuly/null-object\n```\n\nNull-Object:\n\n```typescript\nconst logger = config.log ? realLogger : nullObject\u003cLogger\u003e();\nlogger.info('User signed in'); // Always safe to call any method or access property on a null-object\n```\n\nA bundled noop function just for convenience:\n\n```typescript\nnoop(); // returns `undefined`\n```\n\n## Summary\n\nIt is often useful to silently replace an optional dependency, service, or plugin that is not available in some environment (e.g. stub metrics service in development and stage environments).\n\nThe package provides a safe fallback for any expected object interface by returning a no-op implementation object. It enables cleaner and more robust code due to no null checks or conditional logic.\n\nAny methods called on it are noop, including chained ones. Methods silently accept arbitrary arguments. Access any properties, including nested, silently assign to properties.\n\nCommon use cases include:\n\n- Optional services like logging, analytics, metrics:\n\n  ```typescript\n  const metrics = prod ? nullObject\u003cMetrics\u003e() : devMetrics;\n  ```\n\n- Provide a noop plugin when none is installed:\n  ```typescript\n  const plugin = userProvidedPlugin ?? nullObject\u003cPlugin\u003e();\n  ```\n- Avoid conditional logic in consumers via default parameter:\n  ```typescript\n  function createHandler(callbacks: Callbacks = nullObject\u003cCallbacks\u003e()) {\n   callbacks.onReady(); // Always callable\n  }\n  ```\n- Middleware chains:\n  ```typescript\n  const middleware = [authMiddleware, config.enableAudit ? auditMiddleware : nullObject\u003cMiddleware\u003e()];\n  ```\n\n## Usage\n\nThe signature:\n\n```typescript\nfunction nullObject\u003cT = Record\u003cstring, any\u003e\u003e(name?: string): T;\n```\n\nCall any method, chained if needed, with arbitrary arguments, access any property, including nested and do nothing. Optionally provide null-object with the actual object type to ensure type safety.\n\n```typescript\ntype TSomeActualHandler = { method: any; prop: any }; // May come as third-party type\n\nconst silent = nullObject\u003cTSomeActualHandler\u003e();\n// Could also use it untyped: `const silent = nullObject()`\n\nsilent.method().another('arbitrary', 'args', 123, true);\nsilent.prop.method('arbitrary', 'args', 123, true).more;\n```\n\nHelp debugging with `.toString()` and optional named identity.\n\n```typescript\nconst logger = config.log ? realLogger : nullObject\u003cLogger\u003e();\nconst name = logger.toString(); // name === [NullObject]\n// or\nconst logger = config.log ? realLogger : nullObject\u003cLogger\u003e('Logger');\nconst name = logger.toString(); // name === [NullObject: Logger]\n```\n\nCan silently accept assignments to any property including nested.\n\n```typescript\nconst plugin = userProvidedPlugin ?? nullObject\u003cPlugin\u003e();\nplugin.settings.some = true;\n```\n\n### The Convenience noop Function\n\nThe package also provides the `noop` function so that you do not have to import another package for it.\n\n```typescript\nnoop(); // returns `undefined`\nnoop(1, 'a', true, null); // Accepts arbitrary arguments without throwing\nfunction onEvent(callback: () =\u003e void = noop) {\n callback(); // Safe to call without checking\n}\n```\n\n## Maintenance\n\nThe package is written in TypeScript with the informative JSDoc blocks available on hover for public interface (e.g. in VS Code) for comfortable programmatic usage. The code is carefully crafted with TDD allowing simple extension. The project is production-ready and actively maintained.\n\n### Contributions\n\nFilling issues, questions in Discussions are all welcome.\n\n## License\n\nThis project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwherejuly%2F68-null-object","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwherejuly%2F68-null-object","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwherejuly%2F68-null-object/lists"}