{"id":16383850,"url":"https://github.com/jakesidsmith/existential-proxy","last_synced_at":"2025-02-22T00:23:31.555Z","repository":{"id":57231690,"uuid":"156560491","full_name":"JakeSidSmith/existential-proxy","owner":"JakeSidSmith","description":"Type safe existential property access using ES6 proxies","archived":false,"fork":false,"pushed_at":"2019-01-30T22:54:21.000Z","size":213,"stargazers_count":0,"open_issues_count":2,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-02T19:20:13.451Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://npmjs.com/package/existential-proxy","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/JakeSidSmith.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-11-07T14:41:25.000Z","updated_at":"2018-11-18T20:42:51.000Z","dependencies_parsed_at":"2022-09-04T15:04:45.569Z","dependency_job_id":null,"html_url":"https://github.com/JakeSidSmith/existential-proxy","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JakeSidSmith%2Fexistential-proxy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JakeSidSmith%2Fexistential-proxy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JakeSidSmith%2Fexistential-proxy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JakeSidSmith%2Fexistential-proxy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JakeSidSmith","download_url":"https://codeload.github.com/JakeSidSmith/existential-proxy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240106786,"owners_count":19748687,"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":[],"created_at":"2024-10-11T04:09:50.453Z","updated_at":"2025-02-22T00:23:31.505Z","avatar_url":"https://github.com/JakeSidSmith.png","language":"TypeScript","readme":"# existential-proxy\n\n[![CircleCI](https://circleci.com/gh/JakeSidSmith/existential-proxy.svg?style=svg)](https://circleci.com/gh/JakeSidSmith/existential-proxy)\n\n**Type safe existential property access using ES6 proxies**\n\n## About\n\nThis library gives you the ability to `get`, `set`, or `update` (with an update function) values nested within an object who's keys may be nullable (`undefined` or `null`), without mutating the original input, or having to worry about checking for values' existence.\n\n[Optional chaining](https://github.com/tc39/proposal-optional-chaining) in JavaScript / TypeScript is not yet finalized, and so we need a way to safely access nested values that may or may not exist.\n\nThis library was created for use with TypeScript to give sensible types when accessing nullable nested values without having to specify the types yourself.\n\nAlthough designed with TypeScript in mind, existential-proxy works perfectly well with JavaScript.\n\nUnlike destructuring with default values existential-proxy allows access through values that may be `undefined` or `null` without specifying default values for each level, and allows specifying defaults even when the value may be `null`.\n\nAdditionally, unlike (some) alternatives that allow access to nested properties this library also allows setting or updating values inside a nested object in an immutable way (returning a copy of the input with updated values as necessary).\n\n### Future plans\n\nI intend to add a function to create a selector, much like [reselect](https://github.com/reduxjs/reselect), to allow transforming values only when changed, but using proxies removing the need to null check nested values.\n\n## Installation\n\nInstall existential-proxy with NPM (`-S` will automatically add this to your `package.json`):\n\n```shell\nnpm i existential-proxy -S\n```\n\n## Usage\n\nI'd recommend importing `* as` so that you can easily tell where the functions are coming from, and avoid naming conflicts (as many libraries will use similarly named functions).\n\n```typescript\nimport * as ep from 'existential-proxy';\n```\n\nAlmost all of the examples below will use the following types / object.\n\n```typescript\n// Here's an example object type\ninterface ABC {\n  a: {\n    b?: {\n      c: string;\n    };\n  } | null;\n}\n\n// Here's our example object\nconst abc: ABC = {\n  a: {} // Note that the b key does not exist\n};\n```\n\n### Get\n\nThe `get` function takes 3 arguments:\n\n1. The object from which you wish to retrieve a value\n2. A callback that will be passed a proxy to this object\n3. An optional default value\n\n```typescript\n// Access without defaults\nep.get(abc, (proxy) =\u003e proxy); // ABC\nep.get(abc, (proxy) =\u003e proxy.a); // { b?: { c: string; } } | null\nep.get(abc, (proxy) =\u003e proxy.a.b); // { c: string; } | undefined\nep.get(abc, (proxy) =\u003e proxy.a.b.c); // string | undefined\n\n// Access with defaults\nep.get(abc, (proxy) =\u003e proxy, 'whatever'); // ABC\nep.get(abc, (proxy) =\u003e proxy.a, { b: { c: 'hello' } }); // { b?: { c: string; } } | { b: { c: string; } }\nep.get(abc, (proxy) =\u003e proxy.a.b, { c: 'hello' }); // { c: string; }\nep.get(abc, (proxy) =\u003e proxy.a.b.c, 'hello'); // string\n```\n\n### Set\n\nThe `set` function takes 3 arguments:\n\n1. The object from which you wish to retrieve a value\n2. A callback that will be passed a proxy to this object\n3. The new value to be set at the returned proxy key\n\n\n\n```typescript\n// Will return the provided value (essentially replacing the input object)\nep.set(abc, (proxy) =\u003e proxy, { a: { b: { c: 'hello' } } }); // { a: { b: { c: 'hello' } } }: ABC\n// Will return a copy of the `abc` object with a new `a` value\nep.set(abc, (proxy) =\u003e proxy.a, { b: { c: 'hello' } }); // { a: { b: { c: 'hello' } } }: ABC\n// Will return a copy of the `abc` object with a new `b` value\nep.set(abc, (proxy) =\u003e proxy.a.b, { c: 'hello' }); // { a: { b: { c: 'hello' } } }: ABC\n```\n\n### Update\n\nThe `update` function takes 3 arguments:\n\n1. The object from which you wish to retrieve a value\n2. A callback that will be passed a proxy to this object\n3. An updater function that will be passed the existing value at the returned proxy key, and returns a new value\n\n```typescript\n// Will return the returned value (essentially replacing the input object)\nep.update(abc, (proxy) =\u003e proxy, () =\u003e ({ a: { b: { c: 'hello' } } })); // { a: { b: { c: 'hello' } } }: ABC\n// Will return a copy of the `abc` object with a new `a` value if it is not defined\nep.update(abc, (proxy) =\u003e proxy.a, (a) =\u003e {\n  if (!a) {\n    return { b: { c: 'hello' } };\n  }\n\n  return a;\n}); // { a: { b: { c: 'hello' } } }: ABC\n// Will return a copy of the `abc` object with a new `b` value if it is not defined\nep.update(abc, (proxy) =\u003e proxy.a.b, (b) =\u003e {\n  if (!b) {\n    return { c: 'hello' };\n  }\n\n  return b;\n}); // { a: { b: { c: 'hello' } } }: ABC\n```\n\n## Important notes\n\nWhen using `set` and `update` you should note that:\n\n1. The return type will always match the input type - if keys are nullable, they will still be nullable even if set by one of these functions\n2. Keys that when cast to a number are a valid integer (including negative values) will produce arrays if the parent object is `undefined` or `null`. This is because there is no way to detect if trying to access values from an array or object if the target is not already an array or object (all keys; `.a`, `[0]`, are only available as strings when using a proxy).\n\n## Example of potentially unintended array creation\n\nThis library's `set` and `update` functions may not give you the output you'd expect if you are using integers as keys in objects.\n\n```typescript\ninterface NumKey {\n  a?: {\n    0: string;\n  };\n}\n\nconst numKey: NumKey = {};\n\n// Will create an array when trying to access the `0` key\nep.set(numKey, (proxy) =\u003e proxy.a[0], 'hello'); // { a: ['hello'] }: NumKey\n// Will still create an array when trying to access the `0` key\nep.set(numKey, (proxy) =\u003e proxy.a['0'], 'hello'); // { a: ['hello'] }: NumKey\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjakesidsmith%2Fexistential-proxy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjakesidsmith%2Fexistential-proxy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjakesidsmith%2Fexistential-proxy/lists"}