Created
September 24, 2025 05:17
-
-
Save boneskull/abde40ecc17b6b466aef34e551aaadf3 to your computer and use it in GitHub Desktop.
ESLint rule to disallow direct use of static methods on intrinsics
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * 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