From 2e75e31bff5d7cfd8adc11fd21881bb699a40df9 Mon Sep 17 00:00:00 2001 From: mdm317 Date: Mon, 9 Dec 2024 18:31:28 +0900 Subject: [PATCH 01/12] fix: ignore when constructor is typed array --- .../rules/consistent-generic-constructors.ts | 30 +++++++++++++++++++ .../consistent-generic-constructors.test.ts | 10 +++++++ 2 files changed, 40 insertions(+) diff --git a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts index 9c92cd98188c..dc0321552b2d 100644 --- a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts +++ b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts @@ -33,6 +33,30 @@ export default createRule({ }, defaultOptions: ['constructor'], create(context, [mode]) { + const isTypedArrayReference = (typeNode: TSESTree.TypeNode) => { + const typedArrayNames = [ + 'Int8Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'Int16Array', + 'Uint16Array', + 'Int32Array', + 'Uint32Array', + 'BigInt64Array', + 'BigUint64Array', + 'Float32Array', + 'Float64Array', + ]; + if ( + typeNode.type === AST_NODE_TYPES.TSTypeReference && + typeNode.typeName.type === AST_NODE_TYPES.Identifier && + typedArrayNames.includes(typeNode.typeName.name) + ) { + return true; + } + return false; + }; + return { 'VariableDeclarator,PropertyDefinition,:matches(FunctionDeclaration,FunctionExpression) > AssignmentPattern'( node: @@ -60,6 +84,12 @@ export default createRule({ const [lhsName, rhs] = getLHSRHS(); const lhs = lhsName.typeAnnotation?.typeAnnotation; + // If it's a typed array, we will ignore. + // https://github.com/typescript-eslint/typescript-eslint/issues/10445 + if (lhs && isTypedArrayReference(lhs)) { + return; + } + if ( !rhs || rhs.type !== AST_NODE_TYPES.NewExpression || diff --git a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts index bf49ce384a14..969c57aedef3 100644 --- a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts @@ -40,6 +40,16 @@ class A { ` const a = function (a: Foo = new Foo()) {}; `, + 'let a: Uint8Array = new Uint8Array();', + 'let a: Uint8ClampedArray = new Uint8ClampedArray();', + 'let a: Int16Array = new Int16Array();', + 'let a: Uint16Array = new Uint16Array();', + 'let a: Int32Array = new Int32Array();', + 'let a: Uint32Array = new Uint32Array();', + 'let a: BigInt64Array = new BigInt64Array();', + 'let a: BigUint64Array = new BigUint64Array();', + 'let a: Float32Array = new Float32Array();', + 'let a: Float64Array = new Float64Array();', // type-annotation { code: 'const a = new Foo();', From 34f390ce3e1281549f65413db9877bc5435f1b8b Mon Sep 17 00:00:00 2001 From: mdm317 Date: Mon, 16 Dec 2024 23:24:58 +0900 Subject: [PATCH 02/12] fix: redeclare their own class --- .../rules/consistent-generic-constructors.ts | 48 ++++++++++------- .../consistent-generic-constructors.test.ts | 51 +++++++++++++++++++ 2 files changed, 81 insertions(+), 18 deletions(-) diff --git a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts index dc0321552b2d..e44751de75be 100644 --- a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts +++ b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts @@ -1,5 +1,6 @@ import type { TSESTree } from '@typescript-eslint/utils'; +import { DefinitionType } from '@typescript-eslint/scope-manager'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, nullThrows, NullThrowsReasons } from '../util'; @@ -34,27 +35,25 @@ export default createRule({ defaultOptions: ['constructor'], create(context, [mode]) { const isTypedArrayReference = (typeNode: TSESTree.TypeNode) => { - const typedArrayNames = [ - 'Int8Array', - 'Uint8Array', - 'Uint8ClampedArray', - 'Int16Array', - 'Uint16Array', - 'Int32Array', - 'Uint32Array', - 'BigInt64Array', - 'BigUint64Array', - 'Float32Array', - 'Float64Array', - ]; if ( - typeNode.type === AST_NODE_TYPES.TSTypeReference && - typeNode.typeName.type === AST_NODE_TYPES.Identifier && - typedArrayNames.includes(typeNode.typeName.name) + typeNode.type !== AST_NODE_TYPES.TSTypeReference || + typeNode.typeName.type !== AST_NODE_TYPES.Identifier || + !typedArrayNames.has(typeNode.typeName.name) ) { + return false; + } + + const scope = context.sourceCode.getScope(typeNode); + const variable = scope.set.get(typeNode.typeName.name); + if (!variable) { return true; } - return false; + for (const definition of variable.defs) { + if (definition.type === DefinitionType.ClassName) { + return false; + } + } + return true; }; return { @@ -83,7 +82,6 @@ export default createRule({ } const [lhsName, rhs] = getLHSRHS(); const lhs = lhsName.typeAnnotation?.typeAnnotation; - // If it's a typed array, we will ignore. // https://github.com/typescript-eslint/typescript-eslint/issues/10445 if (lhs && isTypedArrayReference(lhs)) { @@ -178,3 +176,17 @@ export default createRule({ }; }, }); + +const typedArrayNames = new Set([ + 'Int8Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'Int16Array', + 'Uint16Array', + 'Int32Array', + 'Uint32Array', + 'BigInt64Array', + 'BigUint64Array', + 'Float32Array', + 'Float64Array', +]); diff --git a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts index 969c57aedef3..d720fba30091 100644 --- a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts @@ -329,6 +329,31 @@ const a = function (a: Foo = new Foo()) {}; const a = function (a = new Foo()) {}; `, }, + { + code: ` +class Uint8Array { + // ... +} + +let a: Uint8Array = new Uint8Array(); + +export {}; + `, + errors: [ + { + messageId: 'preferConstructor', + }, + ], + output: ` +class Uint8Array { + // ... +} + +let a = new Uint8Array(); + +export {}; + `, + }, { code: 'const a = new Foo();', errors: [ @@ -527,5 +552,31 @@ const a = function (a = new Foo()) {}; const a = function (a: Foo = new Foo()) {}; `, }, + { + code: ` +class Uint8Array { + // ... +} + +let a = new Uint8Array(); + +export {}; + `, + errors: [ + { + messageId: 'preferTypeAnnotation', + }, + ], + options: ['type-annotation'], + output: ` +class Uint8Array { + // ... +} + +let a: Uint8Array = new Uint8Array(); + +export {}; + `, + }, ], }); From d3fd5418bff9566321387229d3da1cb1d701964c Mon Sep 17 00:00:00 2001 From: mdm317 Date: Thu, 13 Mar 2025 00:26:38 +0900 Subject: [PATCH 03/12] Revert "fix: redeclare their own class" This reverts commit 34f390ce3e1281549f65413db9877bc5435f1b8b. --- .../rules/consistent-generic-constructors.ts | 48 +++++++---------- .../consistent-generic-constructors.test.ts | 51 ------------------- 2 files changed, 18 insertions(+), 81 deletions(-) diff --git a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts index e44751de75be..dc0321552b2d 100644 --- a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts +++ b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts @@ -1,6 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; -import { DefinitionType } from '@typescript-eslint/scope-manager'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, nullThrows, NullThrowsReasons } from '../util'; @@ -35,25 +34,27 @@ export default createRule({ defaultOptions: ['constructor'], create(context, [mode]) { const isTypedArrayReference = (typeNode: TSESTree.TypeNode) => { + const typedArrayNames = [ + 'Int8Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'Int16Array', + 'Uint16Array', + 'Int32Array', + 'Uint32Array', + 'BigInt64Array', + 'BigUint64Array', + 'Float32Array', + 'Float64Array', + ]; if ( - typeNode.type !== AST_NODE_TYPES.TSTypeReference || - typeNode.typeName.type !== AST_NODE_TYPES.Identifier || - !typedArrayNames.has(typeNode.typeName.name) + typeNode.type === AST_NODE_TYPES.TSTypeReference && + typeNode.typeName.type === AST_NODE_TYPES.Identifier && + typedArrayNames.includes(typeNode.typeName.name) ) { - return false; - } - - const scope = context.sourceCode.getScope(typeNode); - const variable = scope.set.get(typeNode.typeName.name); - if (!variable) { return true; } - for (const definition of variable.defs) { - if (definition.type === DefinitionType.ClassName) { - return false; - } - } - return true; + return false; }; return { @@ -82,6 +83,7 @@ export default createRule({ } const [lhsName, rhs] = getLHSRHS(); const lhs = lhsName.typeAnnotation?.typeAnnotation; + // If it's a typed array, we will ignore. // https://github.com/typescript-eslint/typescript-eslint/issues/10445 if (lhs && isTypedArrayReference(lhs)) { @@ -176,17 +178,3 @@ export default createRule({ }; }, }); - -const typedArrayNames = new Set([ - 'Int8Array', - 'Uint8Array', - 'Uint8ClampedArray', - 'Int16Array', - 'Uint16Array', - 'Int32Array', - 'Uint32Array', - 'BigInt64Array', - 'BigUint64Array', - 'Float32Array', - 'Float64Array', -]); diff --git a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts index d720fba30091..969c57aedef3 100644 --- a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts @@ -329,31 +329,6 @@ const a = function (a: Foo = new Foo()) {}; const a = function (a = new Foo()) {}; `, }, - { - code: ` -class Uint8Array { - // ... -} - -let a: Uint8Array = new Uint8Array(); - -export {}; - `, - errors: [ - { - messageId: 'preferConstructor', - }, - ], - output: ` -class Uint8Array { - // ... -} - -let a = new Uint8Array(); - -export {}; - `, - }, { code: 'const a = new Foo();', errors: [ @@ -552,31 +527,5 @@ const a = function (a = new Foo()) {}; const a = function (a: Foo = new Foo()) {}; `, }, - { - code: ` -class Uint8Array { - // ... -} - -let a = new Uint8Array(); - -export {}; - `, - errors: [ - { - messageId: 'preferTypeAnnotation', - }, - ], - options: ['type-annotation'], - output: ` -class Uint8Array { - // ... -} - -let a: Uint8Array = new Uint8Array(); - -export {}; - `, - }, ], }); From 7613753f9a3cef463bcdbbe32b9579a0a6ee4ee1 Mon Sep 17 00:00:00 2001 From: mdm317 Date: Thu, 13 Mar 2025 00:26:48 +0900 Subject: [PATCH 04/12] Revert "fix: ignore when constructor is typed array" This reverts commit 2e75e31bff5d7cfd8adc11fd21881bb699a40df9. --- .../rules/consistent-generic-constructors.ts | 30 ------------------- .../consistent-generic-constructors.test.ts | 10 ------- 2 files changed, 40 deletions(-) diff --git a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts index dc0321552b2d..9c92cd98188c 100644 --- a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts +++ b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts @@ -33,30 +33,6 @@ export default createRule({ }, defaultOptions: ['constructor'], create(context, [mode]) { - const isTypedArrayReference = (typeNode: TSESTree.TypeNode) => { - const typedArrayNames = [ - 'Int8Array', - 'Uint8Array', - 'Uint8ClampedArray', - 'Int16Array', - 'Uint16Array', - 'Int32Array', - 'Uint32Array', - 'BigInt64Array', - 'BigUint64Array', - 'Float32Array', - 'Float64Array', - ]; - if ( - typeNode.type === AST_NODE_TYPES.TSTypeReference && - typeNode.typeName.type === AST_NODE_TYPES.Identifier && - typedArrayNames.includes(typeNode.typeName.name) - ) { - return true; - } - return false; - }; - return { 'VariableDeclarator,PropertyDefinition,:matches(FunctionDeclaration,FunctionExpression) > AssignmentPattern'( node: @@ -84,12 +60,6 @@ export default createRule({ const [lhsName, rhs] = getLHSRHS(); const lhs = lhsName.typeAnnotation?.typeAnnotation; - // If it's a typed array, we will ignore. - // https://github.com/typescript-eslint/typescript-eslint/issues/10445 - if (lhs && isTypedArrayReference(lhs)) { - return; - } - if ( !rhs || rhs.type !== AST_NODE_TYPES.NewExpression || diff --git a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts index 969c57aedef3..bf49ce384a14 100644 --- a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts @@ -40,16 +40,6 @@ class A { ` const a = function (a: Foo = new Foo()) {}; `, - 'let a: Uint8Array = new Uint8Array();', - 'let a: Uint8ClampedArray = new Uint8ClampedArray();', - 'let a: Int16Array = new Int16Array();', - 'let a: Uint16Array = new Uint16Array();', - 'let a: Int32Array = new Int32Array();', - 'let a: Uint32Array = new Uint32Array();', - 'let a: BigInt64Array = new BigInt64Array();', - 'let a: BigUint64Array = new BigUint64Array();', - 'let a: Float32Array = new Float32Array();', - 'let a: Float64Array = new Float64Array();', // type-annotation { code: 'const a = new Foo();', From d331b1f7a454e1ca33ac52ea9461e0a37908f100 Mon Sep 17 00:00:00 2001 From: mdm317 Date: Thu, 13 Mar 2025 01:08:32 +0900 Subject: [PATCH 05/12] feat : add ignoreConstructors option --- .../rules/consistent-generic-constructors.ts | 27 ++++++++++++++++--- .../consistent-generic-constructors.test.ts | 4 +++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts index 9c92cd98188c..01b2032545d6 100644 --- a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts +++ b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts @@ -5,7 +5,12 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, nullThrows, NullThrowsReasons } from '../util'; type MessageIds = 'preferConstructor' | 'preferTypeAnnotation'; -type Options = ['constructor' | 'type-annotation']; +type Options = [ + 'constructor' | 'type-annotation', + { + ignoreConstructors?: string[]; + }?, +]; export default createRule({ name: 'consistent-generic-constructors', @@ -29,10 +34,23 @@ export default createRule({ description: 'Which constructor call syntax to prefer.', enum: ['type-annotation', 'constructor'], }, + { + type: 'object', + properties: { + ignoreConstructors: { + type: 'array', + description: + 'A list of constructor names to ignore when enforcing the rule.', + items: { + type: 'string', + }, + }, + }, + }, ], }, - defaultOptions: ['constructor'], - create(context, [mode]) { + defaultOptions: ['constructor', {}], + create(context, [mode, options]) { return { 'VariableDeclarator,PropertyDefinition,:matches(FunctionDeclaration,FunctionExpression) > AssignmentPattern'( node: @@ -71,7 +89,8 @@ export default createRule({ lhs && (lhs.type !== AST_NODE_TYPES.TSTypeReference || lhs.typeName.type !== AST_NODE_TYPES.Identifier || - lhs.typeName.name !== rhs.callee.name) + lhs.typeName.name !== rhs.callee.name || + options?.ignoreConstructors?.includes(lhs.typeName.name)) ) { return; } diff --git a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts index bf49ce384a14..b65d66801116 100644 --- a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts @@ -40,6 +40,10 @@ class A { ` const a = function (a: Foo = new Foo()) {}; `, + { + code: 'let a: Uint8Array = new Uint8Array();', + options: ['constructor', { ignoreConstructors: ['Uint8Array'] }], + }, // type-annotation { code: 'const a = new Foo();', From f3c37b1ba5ff8b42db2761c392a400c5507fc729 Mon Sep 17 00:00:00 2001 From: mdm317 Date: Sat, 15 Mar 2025 00:18:37 +0900 Subject: [PATCH 06/12] chore : change snapshot --- .../rules/consistent-generic-constructors.ts | 1 + .../consistent-generic-constructors.shot | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts index e664ef81abae..9cfb9fbdc424 100644 --- a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts +++ b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts @@ -36,6 +36,7 @@ export default createRule({ }, { type: 'object', + additionalProperties: false, properties: { ignoreConstructors: { type: 'array', diff --git a/packages/eslint-plugin/tests/schema-snapshots/consistent-generic-constructors.shot b/packages/eslint-plugin/tests/schema-snapshots/consistent-generic-constructors.shot index 7f40a340e498..55d5400fbb86 100644 --- a/packages/eslint-plugin/tests/schema-snapshots/consistent-generic-constructors.shot +++ b/packages/eslint-plugin/tests/schema-snapshots/consistent-generic-constructors.shot @@ -9,6 +9,19 @@ exports[`Rule schemas should be convertible to TS types for documentation purpos "description": "Which constructor call syntax to prefer.", "enum": ["constructor", "type-annotation"], "type": "string" + }, + { + "additionalProperties": false, + "properties": { + "ignoreConstructors": { + "description": "A list of constructor names to ignore when enforcing the rule.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" } ] @@ -17,9 +30,15 @@ exports[`Rule schemas should be convertible to TS types for documentation purpos type Options = [ /** Which constructor call syntax to prefer. */ - | 'type-annotation' - /** Which constructor call syntax to prefer. */ - | 'constructor', + ( + | 'type-annotation' + /** Which constructor call syntax to prefer. */ + | 'constructor' + ), + { + /** A list of constructor names to ignore when enforcing the rule. */ + ignoreConstructors?: string[]; + }, ]; " `; From 82e9dfb24c413add6a33e9793a648c8685e361b2 Mon Sep 17 00:00:00 2001 From: mdm317 Date: Sat, 15 Mar 2025 00:18:51 +0900 Subject: [PATCH 07/12] doc : add option description --- .../docs/rules/consistent-generic-constructors.mdx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx b/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx index e4c5e0708398..10ecafef3461 100644 --- a/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx +++ b/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx @@ -79,6 +79,20 @@ const set: Set = new Set(); +### `'ignoreConstructors'` + +{/* insert option description */} + +This is useful when the type signature of a value differs from its type, for example with built-in array-likes. + +The following patterns are considered correct with the options `{ "ignoreConstructors": ["Uint8Array", "UserConstructor"] }`: + +```ts option='"constructor", { "ignoreConstructors": ["Uint8Array", "UserConstructor"] }' showPlaygroundButton +let a: Uint8Array = new Uint8Array(); + +let a: UserConstructor = new UserConstructor(); +``` + ## When Not To Use It You can turn this rule off if you don't want to enforce one kind of generic constructor style over the other. From e32fec8c5b9733b671594f736906e57598af545d Mon Sep 17 00:00:00 2001 From: mdm317 Date: Sat, 15 Mar 2025 01:36:18 +0900 Subject: [PATCH 08/12] doc : change text --- .../rules/consistent-generic-constructors.mdx | 23 +++++++++++++--- .../consistent-generic-constructors.shot | 26 +++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx b/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx index 10ecafef3461..e9f931bf9e7f 100644 --- a/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx +++ b/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx @@ -79,20 +79,37 @@ const set: Set = new Set(); -### `'ignoreConstructors'` +### `ignoreConstructors` {/* insert option description */} -This is useful when the type signature of a value differs from its type, for example with built-in array-likes. +Some constructors have different type signatures between their type and value, for example like Uint8Array. -The following patterns are considered correct with the options `{ "ignoreConstructors": ["Uint8Array", "UserConstructor"] }`: + + + +```ts option='"constructor"' +// Incorrect because Uint8Array has deffenrent type signature and not in ignoreConstructors list +let a: Uint8Array = new Uint8Array(); + +// Incorrect because type arguments appear in type-annotation and not in ignoreConstructors list +let a: UserConstructor = new UserConstructor(); +``` + + + ```ts option='"constructor", { "ignoreConstructors": ["Uint8Array", "UserConstructor"] }' showPlaygroundButton +// Correct because Uint8Array has deffenrent type signature but are included in the ignoreConstructors list. let a: Uint8Array = new Uint8Array(); +// Correct because type arguments appear in type-annotations but are included in the ignoreConstructors list. let a: UserConstructor = new UserConstructor(); ``` + + + ## When Not To Use It You can turn this rule off if you don't want to enforce one kind of generic constructor style over the other. diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/consistent-generic-constructors.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/consistent-generic-constructors.shot index 1e63291e5fae..c2fe05f3f93a 100644 --- a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/consistent-generic-constructors.shot +++ b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/consistent-generic-constructors.shot @@ -44,3 +44,29 @@ const set = new Set(); const set: Set = new Set(); " `; + +exports[`Validating rule docs consistent-generic-constructors.mdx code examples ESLint output 5`] = ` +"Incorrect +Options: "constructor" + +// Incorrect because Uint8Array has deffenrent type signature and not in ignoreConstructors list +let a: Uint8Array = new Uint8Array(); + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The generic type arguments should be specified as part of the constructor type arguments. + +// Incorrect because type arguments appear in type-annotation and not in ignoreConstructors list +let a: UserConstructor = new UserConstructor(); + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The generic type arguments should be specified as part of the constructor type arguments. +" +`; + +exports[`Validating rule docs consistent-generic-constructors.mdx code examples ESLint output 6`] = ` +"Correct +Options: "constructor", { "ignoreConstructors": ["Uint8Array", "UserConstructor"] } + +// Correct because Uint8Array has deffenrent type signature but are included in the ignoreConstructors list. +let a: Uint8Array = new Uint8Array(); + +// Correct because type arguments appear in type-annotations but are included in the ignoreConstructors list. +let a: UserConstructor = new UserConstructor(); +" +`; From 91b65ae1e987d7887f1177ad3ecbc199caf8fb72 Mon Sep 17 00:00:00 2001 From: mdm317 Date: Sat, 15 Mar 2025 01:59:17 +0900 Subject: [PATCH 09/12] typo : ignoreConstructor to ignore --- .../docs/rules/consistent-generic-constructors.mdx | 12 ++++++------ .../src/rules/consistent-generic-constructors.ts | 6 +++--- .../consistent-generic-constructors.shot | 10 +++++----- .../rules/consistent-generic-constructors.test.ts | 2 +- .../consistent-generic-constructors.shot | 4 ++-- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx b/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx index e9f931bf9e7f..0426691ef469 100644 --- a/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx +++ b/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx @@ -79,7 +79,7 @@ const set: Set = new Set(); -### `ignoreConstructors` +### `ignore` {/* insert option description */} @@ -89,21 +89,21 @@ Some constructors have different type signatures between their type and value, f ```ts option='"constructor"' -// Incorrect because Uint8Array has deffenrent type signature and not in ignoreConstructors list +// Incorrect because Uint8Array has deffenrent type signature and not in ignorelist let a: Uint8Array = new Uint8Array(); -// Incorrect because type arguments appear in type-annotation and not in ignoreConstructors list +// Incorrect because type arguments appear in type-annotation and not in ignorelist let a: UserConstructor = new UserConstructor(); ``` -```ts option='"constructor", { "ignoreConstructors": ["Uint8Array", "UserConstructor"] }' showPlaygroundButton -// Correct because Uint8Array has deffenrent type signature but are included in the ignoreConstructors list. +```ts option='"constructor", { "ignore": ["Uint8Array", "UserConstructor"] }' showPlaygroundButton +// Correct because Uint8Array has deffenrent type signature but are included in the ignorelist. let a: Uint8Array = new Uint8Array(); -// Correct because type arguments appear in type-annotations but are included in the ignoreConstructors list. +// Correct because type arguments appear in type-annotations but are included in the ignorelist. let a: UserConstructor = new UserConstructor(); ``` diff --git a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts index 9cfb9fbdc424..c6403b9ca9a6 100644 --- a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts +++ b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts @@ -8,7 +8,7 @@ type MessageIds = 'preferConstructor' | 'preferTypeAnnotation'; type Options = [ 'constructor' | 'type-annotation', { - ignoreConstructors?: string[]; + ignore?: string[]; }?, ]; @@ -38,7 +38,7 @@ export default createRule({ type: 'object', additionalProperties: false, properties: { - ignoreConstructors: { + ignore: { type: 'array', description: 'A list of constructor names to ignore when enforcing the rule.', @@ -97,7 +97,7 @@ export default createRule({ (lhs.type !== AST_NODE_TYPES.TSTypeReference || lhs.typeName.type !== AST_NODE_TYPES.Identifier || lhs.typeName.name !== rhs.callee.name || - options?.ignoreConstructors?.includes(lhs.typeName.name)) + options?.ignore?.includes(lhs.typeName.name)) ) { return; } diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/consistent-generic-constructors.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/consistent-generic-constructors.shot index c2fe05f3f93a..0ba060331449 100644 --- a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/consistent-generic-constructors.shot +++ b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/consistent-generic-constructors.shot @@ -49,11 +49,11 @@ exports[`Validating rule docs consistent-generic-constructors.mdx code examples "Incorrect Options: "constructor" -// Incorrect because Uint8Array has deffenrent type signature and not in ignoreConstructors list +// Incorrect because Uint8Array has deffenrent type signature and not in ignorelist let a: Uint8Array = new Uint8Array(); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The generic type arguments should be specified as part of the constructor type arguments. -// Incorrect because type arguments appear in type-annotation and not in ignoreConstructors list +// Incorrect because type arguments appear in type-annotation and not in ignorelist let a: UserConstructor = new UserConstructor(); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The generic type arguments should be specified as part of the constructor type arguments. " @@ -61,12 +61,12 @@ let a: UserConstructor = new UserConstructor(); exports[`Validating rule docs consistent-generic-constructors.mdx code examples ESLint output 6`] = ` "Correct -Options: "constructor", { "ignoreConstructors": ["Uint8Array", "UserConstructor"] } +Options: "constructor", { "ignore": ["Uint8Array", "UserConstructor"] } -// Correct because Uint8Array has deffenrent type signature but are included in the ignoreConstructors list. +// Correct because Uint8Array has deffenrent type signature but are included in the ignorelist. let a: Uint8Array = new Uint8Array(); -// Correct because type arguments appear in type-annotations but are included in the ignoreConstructors list. +// Correct because type arguments appear in type-annotations but are included in the ignorelist. let a: UserConstructor = new UserConstructor(); " `; diff --git a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts index 71793a63cba8..475aa044939b 100644 --- a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts @@ -47,7 +47,7 @@ const a = function (a: Foo = new Foo()) {}; `, { code: 'let a: Uint8Array = new Uint8Array();', - options: ['constructor', { ignoreConstructors: ['Uint8Array'] }], + options: ['constructor', { ignore: ['Uint8Array'] }], }, // type-annotation { diff --git a/packages/eslint-plugin/tests/schema-snapshots/consistent-generic-constructors.shot b/packages/eslint-plugin/tests/schema-snapshots/consistent-generic-constructors.shot index 55d5400fbb86..525a9b6d2215 100644 --- a/packages/eslint-plugin/tests/schema-snapshots/consistent-generic-constructors.shot +++ b/packages/eslint-plugin/tests/schema-snapshots/consistent-generic-constructors.shot @@ -13,7 +13,7 @@ exports[`Rule schemas should be convertible to TS types for documentation purpos { "additionalProperties": false, "properties": { - "ignoreConstructors": { + "ignore": { "description": "A list of constructor names to ignore when enforcing the rule.", "items": { "type": "string" @@ -37,7 +37,7 @@ type Options = [ ), { /** A list of constructor names to ignore when enforcing the rule. */ - ignoreConstructors?: string[]; + ignore?: string[]; }, ]; " From d81301a577125b3a92e3b8dd3d0f6b4bae4d9e5e Mon Sep 17 00:00:00 2001 From: mdm317 Date: Wed, 24 Sep 2025 23:54:02 +0900 Subject: [PATCH 10/12] fix : docs --- .../consistent-generic-constructors.shot | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/consistent-generic-constructors.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/consistent-generic-constructors.shot index 96e14ff052a8..ca6745508d9e 100644 --- a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/consistent-generic-constructors.shot +++ b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/consistent-generic-constructors.shot @@ -30,11 +30,8 @@ const map: Map = new Map(); const set: Set = new Set(); const set = new Set(); const set: Set = new Set(); -" -`; -exports[`Validating rule docs consistent-generic-constructors.mdx code examples ESLint output 5`] = ` -"Incorrect +Incorrect Options: "constructor" // Incorrect because Uint8Array has deffenrent type signature and not in ignorelist @@ -44,11 +41,8 @@ let a: Uint8Array = new Uint8Array(); // Incorrect because type arguments appear in type-annotation and not in ignorelist let a: UserConstructor = new UserConstructor(); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The generic type arguments should be specified as part of the constructor type arguments. -" -`; -exports[`Validating rule docs consistent-generic-constructors.mdx code examples ESLint output 6`] = ` -"Correct +Correct Options: "constructor", { "ignore": ["Uint8Array", "UserConstructor"] } // Correct because Uint8Array has deffenrent type signature but are included in the ignorelist. @@ -56,5 +50,3 @@ let a: Uint8Array = new Uint8Array(); // Correct because type arguments appear in type-annotations but are included in the ignorelist. let a: UserConstructor = new UserConstructor(); -" -`; From 2bd2fc7d702c264c0e0b392f51ffdb3bddb91eda Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Mon, 3 Nov 2025 17:37:41 -0500 Subject: [PATCH 11/12] Remove allowlist altogether --- .../rules/consistent-generic-constructors.mdx | 31 ---------- .../rules/consistent-generic-constructors.ts | 60 +++++++++++-------- .../consistent-generic-constructors.test.ts | 53 ++++++++++++++-- 3 files changed, 84 insertions(+), 60 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx b/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx index 0426691ef469..e4c5e0708398 100644 --- a/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx +++ b/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx @@ -79,37 +79,6 @@ const set: Set = new Set(); -### `ignore` - -{/* insert option description */} - -Some constructors have different type signatures between their type and value, for example like Uint8Array. - - - - -```ts option='"constructor"' -// Incorrect because Uint8Array has deffenrent type signature and not in ignorelist -let a: Uint8Array = new Uint8Array(); - -// Incorrect because type arguments appear in type-annotation and not in ignorelist -let a: UserConstructor = new UserConstructor(); -``` - - - - -```ts option='"constructor", { "ignore": ["Uint8Array", "UserConstructor"] }' showPlaygroundButton -// Correct because Uint8Array has deffenrent type signature but are included in the ignorelist. -let a: Uint8Array = new Uint8Array(); - -// Correct because type arguments appear in type-annotations but are included in the ignorelist. -let a: UserConstructor = new UserConstructor(); -``` - - - - ## When Not To Use It You can turn this rule off if you don't want to enforce one kind of generic constructor style over the other. diff --git a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts index 471cf7ab68e2..8ba1be086c19 100644 --- a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts +++ b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts @@ -2,15 +2,27 @@ import type { TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; -import { createRule, nullThrows, NullThrowsReasons } from '../util'; +import { + createRule, + isReferenceToGlobalFunction, + nullThrows, + NullThrowsReasons, +} from '../util'; -type MessageIds = 'preferConstructor' | 'preferTypeAnnotation'; -type Options = [ - 'constructor' | 'type-annotation', - { - ignore?: string[]; - }?, -]; +export type MessageIds = 'preferConstructor' | 'preferTypeAnnotation'; +export type Options = ['constructor' | 'type-annotation']; + +const builtInArrays = new Set([ + 'Float32Array', + 'Float64Array', + 'Int16Array', + 'Int32Array', + 'Int8Array', + 'Uint16Array', + 'Uint32Array', + 'Uint8Array', + 'Uint8ClampedArray', +]); export default createRule({ name: 'consistent-generic-constructors', @@ -34,24 +46,10 @@ export default createRule({ description: 'Which constructor call syntax to prefer.', enum: ['type-annotation', 'constructor'], }, - { - type: 'object', - additionalProperties: false, - properties: { - ignore: { - type: 'array', - description: - 'A list of constructor names to ignore when enforcing the rule.', - items: { - type: 'string', - }, - }, - }, - }, ], }, - defaultOptions: ['constructor', {}], - create(context, [mode, options]) { + defaultOptions: ['constructor'], + create(context, [mode]) { return { 'VariableDeclarator,PropertyDefinition,AccessorProperty,:matches(FunctionDeclaration,FunctionExpression) > AssignmentPattern'( node: @@ -82,6 +80,18 @@ export default createRule({ ); } } + + function isBuiltInArray(typeName: TSESTree.Identifier) { + return ( + builtInArrays.has(typeName.name) && + isReferenceToGlobalFunction( + typeName.name, + typeName, + context.sourceCode, + ) + ); + } + const [lhsName, rhs] = getLHSRHS(); const lhs = lhsName.typeAnnotation?.typeAnnotation; @@ -97,7 +107,7 @@ export default createRule({ (lhs.type !== AST_NODE_TYPES.TSTypeReference || lhs.typeName.type !== AST_NODE_TYPES.Identifier || lhs.typeName.name !== rhs.callee.name || - options?.ignore?.includes(lhs.typeName.name)) + isBuiltInArray(lhs.typeName)) ) { return; } diff --git a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts index 61fd41fc150e..f9a49b470805 100644 --- a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts @@ -45,10 +45,38 @@ class A { ` const a = function (a: Foo = new Foo()) {}; `, - { - code: 'let a: Uint8Array = new Uint8Array();', - options: ['constructor', { ignore: ['Uint8Array'] }], - }, + ` +const a: Float32Array = new Float32Array(); +export {}; + `, + ` +const a: Float64Array = new Float64Array(); +export {}; + `, + ` +const a: Int16Array = new Int16Array(); +export {}; + `, + ` +const a: Int8Array = new Int8Array(); +export {}; + `, + ` +const a: Uint16Array = new Uint16Array(); +export {}; + `, + ` +const a: Uint32Array = new Uint32Array(); +export {}; + `, + ` +const a: Uint8Array = new Uint8Array(); +export {}; + `, + ` +const a: Uint8ClampedArray = new Uint8ClampedArray(); +export {}; + `, { code: ` const foo: Foo = new Foo(); @@ -596,5 +624,22 @@ const a = function (a = new Foo()) {}; const a = function (a: Foo = new Foo()) {}; `, }, + { + code: ` +class Float32Array {} +const a: Float32Array = new Float32Array(); +export {}; + `, + errors: [ + { + messageId: 'preferConstructor', + }, + ], + output: ` +class Float32Array {} +const a = new Float32Array(); +export {}; + `, + }, ], }); From 2bd81ac8f6033c4b1f91d18d7cf6f54d0283df20 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Mon, 3 Nov 2025 17:39:57 -0500 Subject: [PATCH 12/12] Update test snapshots --- .../consistent-generic-constructors.shot | 20 --------------- .../consistent-generic-constructors.shot | 25 +++---------------- 2 files changed, 3 insertions(+), 42 deletions(-) diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/consistent-generic-constructors.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/consistent-generic-constructors.shot index ca6745508d9e..4bb5d2eb9433 100644 --- a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/consistent-generic-constructors.shot +++ b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/consistent-generic-constructors.shot @@ -30,23 +30,3 @@ const map: Map = new Map(); const set: Set = new Set(); const set = new Set(); const set: Set = new Set(); - -Incorrect -Options: "constructor" - -// Incorrect because Uint8Array has deffenrent type signature and not in ignorelist -let a: Uint8Array = new Uint8Array(); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The generic type arguments should be specified as part of the constructor type arguments. - -// Incorrect because type arguments appear in type-annotation and not in ignorelist -let a: UserConstructor = new UserConstructor(); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The generic type arguments should be specified as part of the constructor type arguments. - -Correct -Options: "constructor", { "ignore": ["Uint8Array", "UserConstructor"] } - -// Correct because Uint8Array has deffenrent type signature but are included in the ignorelist. -let a: Uint8Array = new Uint8Array(); - -// Correct because type arguments appear in type-annotations but are included in the ignorelist. -let a: UserConstructor = new UserConstructor(); diff --git a/packages/eslint-plugin/tests/schema-snapshots/consistent-generic-constructors.shot b/packages/eslint-plugin/tests/schema-snapshots/consistent-generic-constructors.shot index 6c47fa111765..9220d7089030 100644 --- a/packages/eslint-plugin/tests/schema-snapshots/consistent-generic-constructors.shot +++ b/packages/eslint-plugin/tests/schema-snapshots/consistent-generic-constructors.shot @@ -6,19 +6,6 @@ "description": "Which constructor call syntax to prefer.", "enum": ["constructor", "type-annotation"], "type": "string" - }, - { - "additionalProperties": false, - "properties": { - "ignore": { - "description": "A list of constructor names to ignore when enforcing the rule.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object" } ] @@ -27,13 +14,7 @@ type Options = [ /** Which constructor call syntax to prefer. */ - ( - | 'type-annotation' - /** Which constructor call syntax to prefer. */ - | 'constructor' - ), - { - /** A list of constructor names to ignore when enforcing the rule. */ - ignore?: string[]; - }, + | 'type-annotation' + /** Which constructor call syntax to prefer. */ + | 'constructor', ];