Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ts-optchain

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ts-optchain

Optional Chaining for TypeScript

  • 0.1.8
  • latest
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
7.4K
decreased by-14.65%
Maintainers
1
Weekly downloads
 
Created
Source

Optional Chaining for TypeScript

The 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).

This module includes two optional chaining implementations:

  • ES6 Proxy Implementation: trivial setup, but incompatible with legacy browsers, such as IE 11.
  • TypeScript Custom Code Transformer: faster performance and compatible with legacy browsers.

Installation

npm i --save ts-optchain

ES6 Proxy

No additional configuration is required to use the ES6 Proxy implementation of ts-optchain.

The ES6 Proxy implementation of ts-optchain requires NodeJS >= 6 or compatible JS environment

IMPORTANT: ES6 Proxy is NOT supported by many legacy browsers, including IE 11 and older versions of ReactNative Android!

Consider using one of the following alternative implementations if support for legacy browsers is a requirement.

TypeScript Custom Code Transformer

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.:

// tsconfig.json
{
    "compilerOptions": {
        "plugins": [
            { "transform": "ts-optchain/transform" },
        ]
    },
}

The developer can then build + transform via the command line, webpack, ts-node, etc. Please see the usage instructions.

After setup, the code:

  import { oc } from 'ts-optchain';
  const obj: T = { /* ... */ };
  const value = oc(obj).propA.propB.propC(defaultValue);

...will be automatically transformed to:

  const value =
    (obj != null && obj.propA != null && obj.propA.propB != null && obj.propA.propB.propC != null)
      ? obj.propA.propB.propC
      : defaultValue;

Babel Plugin

For developers using babel with a need for legacy browser support, consider using the derivative project babel-plugin-ts-optchain.

Example Usage

import { oc } from 'ts-optchain';

interface I {
  a?: string;
  b?: {
    d?: string;
  };
  c?: Array<{
    u?: {
      v?: number;
    };
  }>;
  e?: {
    f?: string;
    g?: () => string;
  };
}

const x: I = {
  a: 'hello',
  b: {
    d: 'world',
  },
  c: [{ u: { v: -100 } }, { u: { v: 200 } }, {}, { u: { v: -300 } }],
};

// Here are a few examples of deep object traversal using (a) optional chaining vs
// (b) logic expressions. Each of the following pairs are equivalent in
// result. Note how the benefits of optional chaining accrue with
// the depth and complexity of the traversal.

oc(x).a(); // 'hello'
x.a;

oc(x).b.d(); // 'world'
x.b && x.b.d;

oc(x).c[0].u.v(); // -100
x.c && x.c[0] && x.c[0].u && x.c[0].u.v;

oc(x).c[100].u.v(); // undefined
x.c && x.c[100] && x.c[100].u && x.c[100].u.v;

oc(x).c[100].u.v(1234); // 1234
(x.c && x.c[100] && x.c[100].u && x.c[100].u.v) || 1234;

oc(x).e.f(); // undefined
x.e && x.e.f;

oc(x).e.f('optional default value'); // 'optional default value'
(x.e && x.e.f) || 'optional default value';

// NOTE: working with function value types can be risky. Additional run-time
// checks to verify that object types are functions before invocation are advised!
oc(x).e.g(() => 'Yo Yo')(); // 'Yo Yo'
((x.e && x.e.g) || (() => 'Yo Yo'))();

Problem

When 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:

interface IAddress {
  street?: string;
  city?: string;
  state?: string;
  postalCode?: string;
}

interface IHome {
  address?: IAddress;
  phoneNumber?: string;
}

interface IUser {
  home?: IHome;
}

Without 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:

function getHomeStreet(user: IUser, defaultValue?: string) {
  return (user.home && user.home.address && user.home.address.street) || defaultValue;
}

This implementation is tedious to write. Utilities like lodash's get(...) can help tighten the implementation, namely:

import { get } from 'lodash';

function getHomeStreet(user: IUser, defaultValue?: string) {
  return get(user, 'home.address.street', defaultValue);
}

However, when using tools like lodash the developer loses the benefits of:

  • Compile-time validation of the path home.address.street
  • Compile-time validation of the expected type of the value at home.address.street
  • Development-time code-completion assistance when manipulating the path home.address.street using tools like Visual Studio Code.

Solution

Using the ts-optchain utility, getHomeStreet can be concisely written as:

import { oc } from 'ts-optchain';

function getHomeStreet(user: IUser, defaultValue?: string) {
  return oc(user).home.address.street(defaultValue);
}

Other features of ts-optchain include:

Type Preservation

ts-optchain preserves TypeScript typings through deep tree traversal. For example:

// phoneNumberOptional is of type: string | undefined
const phoneNumberOptional = oc(user).home.phoneNumber();

// phoneNumberRequired is of type: string
const phoneNumberRequired = oc(user).home.phoneNumber('+1.555.123.4567');

Array Types

ts-optchain supports traversal of Array types by index. For example:

interface IItem {
  name?: string;
}

interface ICollection {
  items?: IItem[];
}

function getFirstItemName(collection: ICollection) {
  // Return type: string
  return oc(collection).items[0].name('No Name Item');
}

Function Types

ts-optchain supports traversal to function values. For example:

interface IThing {
  getter?: () => string;
}

const thing: IThing = { ... };
const result = oc(thing).getter(() => 'Default Getter')();

Code-Completion

ts-optchain enables code-completion assistance in popular IDEs such as Visual Studio Code when writing tree-traversal code.

Benchmarks

Comparing the ES6 Proxy implementation vs the TypeScript custom transformer implementation.

Test case:

oc(testData).a.b.c();

Results:

ts-optchaints-optchain/transform
Chrome 722,352,109 ops/s ±1.16%628,693,809 ops/s ±0.44%267x
Safari 12752,298 ops/s ±1.47%1,760,808,177 ops/s ±0.93%2,340x
Firefox 65272,155 ops/s ±4.78%793,869,896 ops/s ±0.82%2,916x

License

ts-optchain is MIT Licensed.

Keywords

FAQs

Package last updated on 31 Jul 2019

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc