diff --git a/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx b/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx index e4c5e0708398..0426691ef469 100644 --- a/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx +++ b/packages/eslint-plugin/docs/rules/consistent-generic-constructors.mdx @@ -79,6 +79,37 @@ 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 a26f1422460e..471cf7ab68e2 100644 --- a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts +++ b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts @@ -4,8 +4,13 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, nullThrows, NullThrowsReasons } from '../util'; -export type MessageIds = 'preferConstructor' | 'preferTypeAnnotation'; -export type Options = ['constructor' | 'type-annotation']; +type MessageIds = 'preferConstructor' | 'preferTypeAnnotation'; +type Options = [ + 'constructor' | 'type-annotation', + { + ignore?: string[]; + }?, +]; export default createRule({ name: 'consistent-generic-constructors', @@ -29,10 +34,24 @@ 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]) { + defaultOptions: ['constructor', {}], + create(context, [mode, options]) { return { 'VariableDeclarator,PropertyDefinition,AccessorProperty,:matches(FunctionDeclaration,FunctionExpression) > AssignmentPattern'( node: @@ -77,7 +96,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?.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 4bb5d2eb9433..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,3 +30,23 @@ 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/rules/consistent-generic-constructors.test.ts b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts index 1a573186b1ae..61fd41fc150e 100644 --- a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts @@ -45,6 +45,10 @@ class A { ` const a = function (a: Foo = new Foo()) {}; `, + { + code: 'let a: Uint8Array = new Uint8Array();', + options: ['constructor', { ignore: ['Uint8Array'] }], + }, { code: ` const foo: Foo = new Foo(); 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 9220d7089030..6c47fa111765 100644 --- a/packages/eslint-plugin/tests/schema-snapshots/consistent-generic-constructors.shot +++ b/packages/eslint-plugin/tests/schema-snapshots/consistent-generic-constructors.shot @@ -6,6 +6,19 @@ "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" } ] @@ -14,7 +27,13 @@ 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. */ + ignore?: string[]; + }, ];