{"id":13660879,"url":"https://github.com/rimeto/ts-optchain","last_synced_at":"2025-04-24T23:30:58.346Z","repository":{"id":54297617,"uuid":"143962408","full_name":"rimeto/ts-optchain","owner":"rimeto","description":"Optional Chaining for TypeScript","archived":true,"fork":false,"pushed_at":"2021-02-25T21:51:59.000Z","size":158,"stargazers_count":577,"open_issues_count":17,"forks_count":17,"subscribers_count":22,"default_branch":"master","last_synced_at":"2024-11-10T15:44:35.590Z","etag":null,"topics":["code-transformation","optional-chaining","typescript"],"latest_commit_sha":null,"homepage":null,"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/rimeto.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}},"created_at":"2018-08-08T04:49:30.000Z","updated_at":"2024-10-02T01:55:24.000Z","dependencies_parsed_at":"2022-08-13T11:20:27.910Z","dependency_job_id":null,"html_url":"https://github.com/rimeto/ts-optchain","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rimeto%2Fts-optchain","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rimeto%2Fts-optchain/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rimeto%2Fts-optchain/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rimeto%2Fts-optchain/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rimeto","download_url":"https://codeload.github.com/rimeto/ts-optchain/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250727501,"owners_count":21477322,"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":["code-transformation","optional-chaining","typescript"],"created_at":"2024-08-02T05:01:26.915Z","updated_at":"2025-04-24T23:30:58.061Z","avatar_url":"https://github.com/rimeto.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"----\n**This project is now deprecated with [Optional Chaining](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html) support included in TypeScript 3.7 and later.**\n----\n\n\n# Optional Chaining for TypeScript\n\nThe `ts-optchain` module is an implementation of optional chaining with default value support for TypeScript. `ts-optchain` helps the developer produce less verbose code while preserving TypeScript typings when traversing deep property structures. This library serves as an interim solution pending JavaScript/TypeScript built-in support for optional chaining in future releases (see: [Related Resources](#related)).\n\nThis module includes two optional chaining implementations:\n\n* **ES6 Proxy Implementation**: trivial setup, but *incompatible with legacy browsers, such as IE 11.*\n* **TypeScript Custom Code Transformer**: [faster performance](#benchmarks) and compatible with legacy browsers.\n\n## Installation\n\n```bash\nnpm i --save ts-optchain\n```\n\n### ES6 Proxy\n\nNo additional configuration is required to use the ES6 Proxy implementation of `ts-optchain`.\n\nThe ES6 Proxy implementation of `ts-optchain` requires NodeJS \u003e= 6 or [compatible JS environment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#Browser_compatibility)\n\n**IMPORTANT: ES6 Proxy is NOT supported by many legacy browsers, including IE 11 and older versions of ReactNative Android!**\n\nConsider using one of the following alternative implementations if support for legacy browsers is a requirement.\n\n### TypeScript Custom Code Transformer\n\n[TTypescript](https://github.com/cevek/ttypescript) is a tool allows the developer to apply the TypeScript custom transformer automatically at build time. Configuration is as simple as adding the `plugins` property to `compilerOptions` in `tsconfig.json`, e.g.:\n\n```typescript\n// tsconfig.json\n{\n    \"compilerOptions\": {\n        \"plugins\": [\n            { \"transform\": \"ts-optchain/transform\" },\n        ]\n    },\n}\n```\n\nThe developer can then build + transform via the command line, webpack, ts-node, etc. Please see the [usage instructions](https://github.com/cevek/ttypescript#how-to-use).\n\nAfter setup, the code:\n\n```typescript\n  import { oc } from 'ts-optchain';\n  const obj: T = { /* ... */ };\n  const value = oc(obj).propA.propB.propC(defaultValue);\n```\n\n...will be automatically transformed to:\n\n```typescript\n  const value =\n    (obj != null \u0026\u0026 obj.propA != null \u0026\u0026 obj.propA.propB != null \u0026\u0026 obj.propA.propB.propC != null)\n      ? obj.propA.propB.propC\n      : defaultValue;\n```\n\n### Babel Plugin\n\nFor developers using `babel` with a need for legacy browser support, consider using the derivative project [`babel-plugin-ts-optchain`](https://github.com/epeli/babel-plugin-ts-optchain).\n\n## Example Usage\n\n```typescript\nimport { oc } from 'ts-optchain';\n\ninterface I {\n  a?: string;\n  b?: {\n    d?: string;\n  };\n  c?: Array\u003c{\n    u?: {\n      v?: number;\n    };\n  }\u003e;\n  e?: {\n    f?: string;\n    g?: () =\u003e string;\n  };\n}\n\nconst x: I = {\n  a: 'hello',\n  b: {\n    d: 'world',\n  },\n  c: [{ u: { v: -100 } }, { u: { v: 200 } }, {}, { u: { v: -300 } }],\n};\n\n// Here are a few examples of deep object traversal using (a) optional chaining vs\n// (b) logic expressions. Each of the following pairs are equivalent in\n// result. Note how the benefits of optional chaining accrue with\n// the depth and complexity of the traversal.\n\noc(x).a(); // 'hello'\nx.a;\n\noc(x).b.d(); // 'world'\nx.b \u0026\u0026 x.b.d;\n\noc(x).c[0].u.v(); // -100\nx.c \u0026\u0026 x.c[0] \u0026\u0026 x.c[0].u \u0026\u0026 x.c[0].u.v;\n\noc(x).c[100].u.v(); // undefined\nx.c \u0026\u0026 x.c[100] \u0026\u0026 x.c[100].u \u0026\u0026 x.c[100].u.v;\n\noc(x).c[100].u.v(1234); // 1234\n(x.c \u0026\u0026 x.c[100] \u0026\u0026 x.c[100].u \u0026\u0026 x.c[100].u.v) || 1234;\n\noc(x).e.f(); // undefined\nx.e \u0026\u0026 x.e.f;\n\noc(x).e.f('optional default value'); // 'optional default value'\n(x.e \u0026\u0026 x.e.f) || 'optional default value';\n\n// NOTE: working with function value types can be risky. Additional run-time\n// checks to verify that object types are functions before invocation are advised!\noc(x).e.g(() =\u003e 'Yo Yo')(); // 'Yo Yo'\n((x.e \u0026\u0026 x.e.g) || (() =\u003e 'Yo Yo'))();\n```\n\n## Problem\n\nWhen traversing tree-like property structures, the developer often must check for existence of intermediate nodes to avoid run-time exceptions. While TypeScript is helpful in requiring the necessary existence checks at compile-time, the final code is still quite cumbersome. For example, given the interfaces:\n\n```typescript\ninterface IAddress {\n  street?: string;\n  city?: string;\n  state?: string;\n  postalCode?: string;\n}\n\ninterface IHome {\n  address?: IAddress;\n  phoneNumber?: string;\n}\n\ninterface IUser {\n  home?: IHome;\n}\n```\n\nWithout support for optional chaining built into TypeScript yet, an implementation for a method to extract the home street string from this structure would look like:\n\n```typescript\nfunction getHomeStreet(user: IUser, defaultValue?: string) {\n  return (user.home \u0026\u0026 user.home.address \u0026\u0026 user.home.address.street) || defaultValue;\n}\n```\n\nThis implementation is tedious to write. Utilities like `lodash`'s `get(...)` can help tighten the implementation, namely:\n\n```typescript\nimport { get } from 'lodash';\n\nfunction getHomeStreet(user: IUser, defaultValue?: string) {\n  return get(user, 'home.address.street', defaultValue);\n}\n```\n\nHowever, when using tools like `lodash` the developer loses the benefits of:\n\n- Compile-time validation of the path `home.address.street`\n- Compile-time validation of the expected type of the value at `home.address.street`\n- Development-time code-completion assistance when manipulating the path `home.address.street` using tools like Visual Studio Code.\n\n## Solution\n\nUsing the `ts-optchain` utility, `getHomeStreet` can be concisely written as:\n\n```typescript\nimport { oc } from 'ts-optchain';\n\nfunction getHomeStreet(user: IUser, defaultValue?: string) {\n  return oc(user).home.address.street(defaultValue);\n}\n```\n\nOther features of `ts-optchain` include:\n\n### Type Preservation\n\n`ts-optchain` preserves TypeScript typings through deep tree traversal. For example:\n\n```typescript\n// phoneNumberOptional is of type: string | undefined\nconst phoneNumberOptional = oc(user).home.phoneNumber();\n\n// phoneNumberRequired is of type: string\nconst phoneNumberRequired = oc(user).home.phoneNumber('+1.555.123.4567');\n```\n\n### Array Types\n\n`ts-optchain` supports traversal of Array types by index. For example:\n\n```typescript\ninterface IItem {\n  name?: string;\n}\n\ninterface ICollection {\n  items?: IItem[];\n}\n\nfunction getFirstItemName(collection: ICollection) {\n  // Return type: string\n  return oc(collection).items[0].name('No Name Item');\n}\n```\n\n### Function Types\n\n`ts-optchain` supports traversal to function values. For example:\n\n```typescript\ninterface IThing {\n  getter?: () =\u003e string;\n}\n\nconst thing: IThing = { ... };\nconst result = oc(thing).getter(() =\u003e 'Default Getter')();\n```\n\n### Code-Completion\n\n`ts-optchain` enables code-completion assistance in popular IDEs such as Visual Studio Code when writing tree-traversal code.\n\n## \u003ca name=\"benchmarks\"\u003e\u003c/a\u003eBenchmarks\n\nComparing the ES6 Proxy implementation vs the TypeScript custom transformer implementation.\n\n### Test case:\n\n```typescript\noc(testData).a.b.c();\n```\n\n### Results:\n\n||`ts-optchain`|`ts-optchain/transform`||\n|--|--:|--:|--:|\n|Chrome 72|`2,352,109 ops/s ±1.16%`|`628,693,809 ops/s ±0.44%`|`267x`|\n|Safari 12|`752,298 ops/s ±1.47%`|`1,760,808,177 ops/s ±0.93%`|`2,340x`|\n|Firefox 65|`272,155 ops/s ±4.78%`|`793,869,896 ops/s ±0.82%`|`2,916x`|\n\n\n## \u003ca name=\"related\"\u003e\u003c/a\u003eRelated Resources\n\n- [Optional Chaining in TypeScript](https://medium.com/inside-rimeto/optional-chaining-in-typescript-622c3121f99b)\n- [Optional Chaining for JavaScript (TC39 Proposal)](https://github.com/tc39/proposal-optional-chaining)\n\n## License\n\n`ts-optchain` is MIT Licensed.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frimeto%2Fts-optchain","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frimeto%2Fts-optchain","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frimeto%2Fts-optchain/lists"}