npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

eslint-plugin-no-optional-call

v1.0.3

Published

An ESLint plugin to disable the use of the optional-call operator.

Downloads

5

Readme

ESLint Plugin: no-optional-call

npm Module

Overview

The no-optional-call ESLint plugin provides a single rule (non-configurable) that disallows any usage of the ?.( optional-call form.

Unlike the related ?. / ?.[ optional-chaining operators (which are quite useful), the ?.( optional-call operator is total junk, and should never have been added to the language -- at least, not how it was designed.

This plugin allows you to ensure that it never creeps into your project by accident.

Explanation

The supposed usage of this operator is like this:

obj?.func?.(42);

The first ?. is optional-chaining (which is fine!), but the second ?.( is an optional-call (which is bad). But importantly, this is not necessarily an object/method feature, as the optional-call can be used with a single identifier like this:

func?.(42);

In both cases, here's the equivalent code this ?.( operator purports to replace:

if (obj.func != null) {
    obj.func(42);
}

if (func != null) {
    func(42);
}

The usage of != null here is on purpose. It's non-nullish checking, meaning that it avoids null and undefined, but no other values.

WARNING: a common misconception is that the ?.( operator is actually intended for replacing this kind of code (which is a bit more common/relaxed):

obj.func && obj.func(42);

// or:
if (obj.func) {
    obj.func(42);
}

// **********

func && func(42);

// or:
if (func) {
    func(42);
}

These are subtly but importantly different than the previous != null forms. That's the first gotcha! If your existing code has relied on avoiding falsy values in obj.func / func other than the nullish values (null, undefined), such as false, "", NaN, or 0, then switching to ?.( will break your code, since the ?.( operator only stops at null and undefined values.

Oops!

But here's what's worse, what really dooms this ?.( feature, and why you should avoid ever using it in your programs (and rely on this plugin to ensure you don't).

This operator looks like what it's doing is providing a "safe call" type of operator (which exists in other programming languages). It seems like it's making sure that the value is callable (is actually a function) before calling it.

To the untrained eye that's not paying close attention, ?.( looks like it should be doing this:

if (typeof obj.func == "function") {
    obj.func(42);
}

if (typeof func == "function") {
    func(42);
}

But it doesn't! It only avoids null / undefined values.

If obj.func / func holds any non-function truthy value (strings, numbers, objects, arrays, dates, regular expressions, etc), then ?.( will attempt to execute call that value as if it was a function, which of course will fail because none of those are functions.

In other words, all falsy values except null and undefined, and all truthy values besides functions themselves, are all traps where the ?.( operator is going to fall over and break your program.

Types!?

I know a bunch of you are yelling at me that TypeScript solves this problem, because it makes sure all those other value-types are not in obj.func / func.

Here's my simple rebuttal: I call utter B.S. on the design of any JS feature which is full of (technically, an infinite number of) gotcha footguns by itself, and only operates sensibly if you also use TypeScript.

It'd be fine if TypeScript wanted to add this feature. Use it there to your heart's content! But if you're writing only JS, you should never, ever, ever, ever... use this ?.( feature.

Enabling The Plugin

To use no-optional-call, load it as a plugin into ESLint and configure the rules as desired.

.eslintrc.json

To load the plugin and enable its rules via a local or global .eslintrc.json configuration file:

"plugins": [
    "no-optional-call"
],
"rules": {
    "no-optional-call/default": "error"
}

package.json

To load the plugin and enable its rules via a project's package.json:

"eslintConfig": {
    "plugins": [
        "no-optional-call"
    ],
    "rules": {
        "no-optional-call/default": "error"
    }
}

ESLint CLI parameters

To load the plugin and enable its rules via ESLint CLI parameters, use --plugin and --rule flags:

eslint .. --plugin='no-optional-call' --rule='no-optional-call/default: error' ..

ESLint Node API

To use this plugin in Node.js with the ESLint API, require the npm module, and then (for example) pass the rule's definition to Linter#defineRule(..), similar to:

var noOptionalCall = require("eslint-plugin-no-optional-call");

// ..

var eslinter = new (require("eslint").Linter)();

eslinter.defineRule("no-optional-call/default",noOptionalCall.rules.default);

Then lint some code like this:

eslinter.verify(".. some code ..",{
    rules: {
        "no-optional-call/default": "error",
    }
});

Inline Comments

Once the plugin is loaded, the rule can be configured using inline code comments if desired, such as:

/* eslint "no-optional-call/default": "error" */

npm Package

To use this plugin with a global install of ESLint (recommended):

npm install -g eslint-plugin-no-optional-call

To use this plugin with a local install of ESLint:

npm install eslint-plugin-no-optional-call

License

All code and documentation are (c) 2022 Kyle Simpson and released under the MIT License. A copy of the MIT License is also included.

NOTE: This package was heavily inspired by eslint-plugin-no-pipe by @arendjr.