Skip to content

Instantly share code, notes, and snippets.

@boneskull
Created September 24, 2025 05:17
Show Gist options
  • Save boneskull/abde40ecc17b6b466aef34e551aaadf3 to your computer and use it in GitHub Desktop.
Save boneskull/abde40ecc17b6b466aef34e551aaadf3 to your computer and use it in GitHub Desktop.
ESLint rule to disallow direct use of static methods on intrinsics
/**
* ESLint rule to require static methods of intrinsics to be dereferenced before
* use
*
* This rule enforces that static methods of JavaScript intrinsics (like
* Object.keys, Array.from, etc.) should be destructured before use instead of
* being accessed directly. This can help with performance and bundle size by
* allowing these methods to be reused throughout a module.
*
* @example
*
* ```js
* // ❌ Bad
* const keys = Object.keys(obj);
* const arr = Array.from(iterable);
*
* // ✅ Good
* const { keys } = Object;
* const { from } = Array;
* const objKeys = keys(obj);
* const arr = from(iterable);
* ```
*
* @packageDocumentation
*/
import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils';
const { freeze } = Object;
// Intrinsics found in the codebase that have static methods
const INTRINSICS_WITH_STATIC_METHODS = freeze(
new Set(
/** @type {const} */ ([
'Array',
'ArrayBuffer',
'BigInt',
'Boolean',
'DataView',
'Date',
'Error',
'Float32Array',
'Float64Array',
'Int16Array',
'Int32Array',
'Int8Array',
'Intl',
'JSON',
'Map',
'Math',
'Number',
'Object',
'Promise',
'Proxy',
'Reflect',
'RegExp',
'Set',
'String',
'Symbol',
'Uint16Array',
'Uint32Array',
'Uint8Array',
'WeakMap',
'WeakSet',
'console',
]),
),
);
/**
* Rule to require static methods of intrinsics to be dereferenced before use
*/
export const rule = ESLintUtils.RuleCreator.withoutDocs({
create(context, [options]) {
const intrinsics = new Set(
options?.intrinsics ?? INTRINSICS_WITH_STATIC_METHODS,
);
const allowConsole = options?.allowConsole ?? false;
// Skip console if allowed
if (allowConsole) {
intrinsics.delete('console');
}
return {
MemberExpression(node) {
// Check if this is a static method call on an intrinsic
if (
node.object.type === AST_NODE_TYPES.Identifier &&
intrinsics.has(/** @type {any} */ (node.object.name)) &&
node.property.type === AST_NODE_TYPES.Identifier &&
!node.computed
) {
const intrinsic = node.object.name;
const method = node.property.name;
context.report({
data: {
intrinsic,
method,
},
messageId: 'preferDestructuring',
node,
});
}
},
};
},
defaultOptions: [
{
allowConsole: false,
intrinsics: [...INTRINSICS_WITH_STATIC_METHODS],
},
],
meta: {
docs: {
description:
'Require static methods of intrinsics to be dereferenced before use',
},
messages: {
preferDestructuring:
'Prefer destructuring static method "{{method}}" from "{{intrinsic}}" instead of direct access',
},
schema: [
{
additionalProperties: false,
properties: {
allowConsole: {
default: false,
description:
'Whether to allow console.* methods without destructuring',
type: 'boolean',
},
intrinsics: {
description:
'List of intrinsic names to check for static method usage',
items: {
type: 'string',
},
type: 'array',
},
},
type: 'object',
},
],
type: 'suggestion',
},
});
export default rule;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment