From 592e2826abfc91e878140b275bcc4f128dcf9da0 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Mon, 7 Sep 2020 21:39:29 -0400 Subject: [PATCH 1/4] feat(eslint-plugin): add no-unnecessary-type-constraint rule --- packages/eslint-plugin/README.md | 1 + .../rules/no-unnecessary-type-constraint.md | 55 ++++ packages/eslint-plugin/src/configs/all.ts | 1 + packages/eslint-plugin/src/rules/index.ts | 2 + .../rules/no-unnecessary-type-constraint.ts | 116 ++++++++ .../no-unnecessary-type-constraint.test.ts | 281 ++++++++++++++++++ 6 files changed, 456 insertions(+) create mode 100644 packages/eslint-plugin/docs/rules/no-unnecessary-type-constraint.md create mode 100644 packages/eslint-plugin/src/rules/no-unnecessary-type-constraint.ts create mode 100644 packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 63b5b6e7f32..2cebef01445 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -142,6 +142,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int | [`@typescript-eslint/no-unnecessary-qualifier`](./docs/rules/no-unnecessary-qualifier.md) | Warns when a namespace qualifier is unnecessary | | :wrench: | :thought_balloon: | | [`@typescript-eslint/no-unnecessary-type-arguments`](./docs/rules/no-unnecessary-type-arguments.md) | Enforces that type arguments will not be used if not required | | :wrench: | :thought_balloon: | | [`@typescript-eslint/no-unnecessary-type-assertion`](./docs/rules/no-unnecessary-type-assertion.md) | Warns if a type assertion does not change the type of an expression | :heavy_check_mark: | :wrench: | :thought_balloon: | +| [`@typescript-eslint/no-unnecessary-type-constraint`](./docs/rules/no-unnecessary-type-constraint.md) | Disallows unnecessary constraints on generic types | | :wrench: | :thought_balloon: | | [`@typescript-eslint/no-unsafe-assignment`](./docs/rules/no-unsafe-assignment.md) | Disallows assigning any to variables and properties | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/no-unsafe-call`](./docs/rules/no-unsafe-call.md) | Disallows calling an any type value | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/no-unsafe-member-access`](./docs/rules/no-unsafe-member-access.md) | Disallows member access on any typed variables | :heavy_check_mark: | | :thought_balloon: | diff --git a/packages/eslint-plugin/docs/rules/no-unnecessary-type-constraint.md b/packages/eslint-plugin/docs/rules/no-unnecessary-type-constraint.md new file mode 100644 index 00000000000..8656ace566e --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-unnecessary-type-constraint.md @@ -0,0 +1,55 @@ +# Disallows unnecessary constraints on generic types (`no-unnecessary-type-constraint`) + +## Rule Details + +Type parameters (``) may be "constrained" with an `extends` keyword ([docs](https://www.typescriptlang.org/docs/handbook/generics.html#generic-constraints)). +When not provided, type parameters happen to default to: + +- As of TypeScript 3.9: `unknown` ([docs](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-9.html#type-parameters-that-extend-any-no-longer-act-as-any)) +- Before that, as of 3.5: `any` ([docs](https://devblogs.microsoft.com/typescript/announcing-typescript-3-5/#breaking-changes)) + +It is therefore redundant to `extend` from these types in later versions of TypeScript. + +Examples of **incorrect** code for this rule: + +```ts +interface FooAny {} +interface FooUnknown {} + +type BarAny = {}; +type BarUnknown = {}; + +class BazAny { + quxUnknown() {} +} + +class BazUnknown { + quxUnknown() {} +} + +const QuuxAny = () => {}; +const QuuxUnknown = () => {}; + +function QuuzAny() {} +function QuuzUnknown() {} +``` + +Examples of **correct** code for this rule: + +```ts +interface Foo {} + +type Bar = {}; + +class Baz { + qux { } +} + +const Quux = () => {}; + +function Quuz() {} +``` + +## When Not To Use It + +If you don't care about the specific styles of your type constraints, or never use them in the first place, then you will not need this rule. diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts index 7e457d7aa1f..9fd36343890 100644 --- a/packages/eslint-plugin/src/configs/all.ts +++ b/packages/eslint-plugin/src/configs/all.ts @@ -87,6 +87,7 @@ export = { '@typescript-eslint/no-type-alias': 'error', '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error', '@typescript-eslint/no-unnecessary-condition': 'error', + '@typescript-eslint/no-unnecessary-type-constraint': 'error', '@typescript-eslint/no-unnecessary-qualifier': 'error', '@typescript-eslint/no-unnecessary-type-arguments': 'error', '@typescript-eslint/no-unnecessary-type-assertion': 'error', diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index fa8dba93ed1..fbed7cc723b 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -64,6 +64,7 @@ import noUnnecessaryCondition from './no-unnecessary-condition'; import noUnnecessaryQualifier from './no-unnecessary-qualifier'; import noUnnecessaryTypeArguments from './no-unnecessary-type-arguments'; import noUnnecessaryTypeAssertion from './no-unnecessary-type-assertion'; +import noUnnecessaryTypeConstraint from './no-unnecessary-type-constraint'; import noUnsafeAssignment from './no-unsafe-assignment'; import noUnsafeCall from './no-unsafe-call'; import noUnsafeMemberAccess from './no-unsafe-member-access'; @@ -170,6 +171,7 @@ export default { 'no-unnecessary-qualifier': noUnnecessaryQualifier, 'no-unnecessary-type-arguments': noUnnecessaryTypeArguments, 'no-unnecessary-type-assertion': noUnnecessaryTypeAssertion, + 'no-unnecessary-type-constraint': noUnnecessaryTypeConstraint, 'no-unsafe-assignment': noUnsafeAssignment, 'no-unsafe-call': noUnsafeCall, 'no-unsafe-member-access': noUnsafeMemberAccess, diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-type-constraint.ts b/packages/eslint-plugin/src/rules/no-unnecessary-type-constraint.ts new file mode 100644 index 00000000000..4f98a438a66 --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-constraint.ts @@ -0,0 +1,116 @@ +import { TSESTree } from '@typescript-eslint/experimental-utils'; +import * as semver from 'semver'; +import * as ts from 'typescript'; +import * as util from '../util'; + +type MakeRequired = Omit & + Required>; + +type TypeParameterWithConstraint = MakeRequired< + TSESTree.TSTypeParameter, + 'constraint' +>; + +type KeywordFilter = (type: ts.Type) => boolean; + +const is3dot5 = semver.satisfies( + ts.version, + `>= 3.5.0 || >= 3.5.1-rc || >= 3.5.0-beta`, + { + includePrerelease: true, + }, +); + +const is3dot9 = + is3dot5 && + semver.satisfies(ts.version, `>= 3.9.0 || >= 3.9.1-rc || >= 3.9.0-beta`, { + includePrerelease: true, + }); + +export default util.createRule({ + name: 'no-unnecessary-type-constraint', + meta: { + docs: { + category: 'Best Practices', + description: 'Disallows unnecessary constraints on generic types', + recommended: false, + requiresTypeChecking: true, + suggestion: true, + }, + fixable: 'code', + messages: { + unnecessaryConstraint: + 'Constraining a generic type to {{constraint}} does nothing and is unnecessary.', + }, + schema: [], + type: 'suggestion', + }, + defaultOptions: [], + create(context) { + const parserServices = util.getParserServices(context); + const checker = parserServices.program.getTypeChecker(); + + const keywordFilters: [KeywordFilter, string][] = []; + if (is3dot5) { + keywordFilters.push([util.isTypeUnknownType, 'unknown']); + + if (is3dot9) { + keywordFilters.push([util.isTypeAnyType, 'any']); + } + } + + if (!keywordFilters.length) { + return {}; + } + + const inJsx = context.getFilename().toLowerCase().endsWith('tsx'); + + const report = ( + node: TypeParameterWithConstraint, + constraint: string, + inArrowFunction: boolean, + ): void => { + context.report({ + data: { constraint }, + fix(fixer) { + return fixer.replaceTextRange( + [node.name.range[1], node.constraint.range[1]], + inArrowFunction && inJsx ? ',' : '', + ); + }, + messageId: 'unnecessaryConstraint', + node, + }); + }; + + const checkNode = ( + node: TypeParameterWithConstraint, + inArrowFunction: boolean, + ): void => { + const constraint = parserServices.esTreeNodeToTSNodeMap.get( + node.constraint, + ); + const constraintType = checker.getTypeAtLocation(constraint); + + for (const [filter, type] of keywordFilters) { + if (filter(constraintType)) { + report(node, type, inArrowFunction); + return; + } + } + }; + + return { + ':not(ArrowFunctionExpression) > TSTypeParameterDeclaration > TSTypeParameter[constraint]'( + node: TypeParameterWithConstraint, + ): void { + checkNode(node, false); + }, + 'ArrowFunctionExpression > TSTypeParameterDeclaration > TSTypeParameter[constraint]'( + node: TypeParameterWithConstraint, + ): void { + checkNode(node, true); + }, + }; + }, +}); diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts new file mode 100644 index 00000000000..6a6a4b2b3d8 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts @@ -0,0 +1,281 @@ +import rule from '../../src/rules/no-unnecessary-type-constraint'; +import { RuleTester, getFixturesRootDir, noFormat } from '../RuleTester'; + +const rootDir = getFixturesRootDir(); + +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 2018, + tsconfigRootDir: rootDir, + project: './tsconfig.json', + }, + parser: '@typescript-eslint/parser', +}); + +ruleTester.run('no-unnecessary-type-constraint', rule, { + valid: [ + 'function data() {}', + 'function data() {}', + 'function data() {}', + 'function data() {}', + 'function data() {}', + 'const data = () => {};', + 'const data = () => {};', + 'const data = () => {};', + 'const data = () => {};', + 'const data = () => {};', + ], + invalid: [ + { + code: 'function data() {}', + errors: [ + { + data: { constraint: 'any' }, + messageId: 'unnecessaryConstraint', + endColumn: 28, + column: 15, + line: 1, + }, + ], + output: 'function data() {}', + }, + { + code: 'function data() {}', + errors: [ + { + data: { constraint: 'any' }, + messageId: 'unnecessaryConstraint', + endColumn: 28, + column: 15, + line: 1, + }, + ], + output: 'function data() {}', + }, + { + code: 'function data() {}', + errors: [ + { + data: { constraint: 'any' }, + messageId: 'unnecessaryConstraint', + endColumn: 31, + column: 18, + line: 1, + }, + ], + output: 'function data() {}', + }, + { + code: 'function data() {}', + errors: [ + { + data: { constraint: 'any' }, + messageId: 'unnecessaryConstraint', + endColumn: 28, + column: 15, + line: 1, + }, + ], + output: 'function data() {}', + }, + { + code: 'const data = () => {};', + errors: [ + { + data: { constraint: 'any' }, + messageId: 'unnecessaryConstraint', + endColumn: 28, + column: 15, + line: 1, + }, + ], + filename: 'react.tsx', + output: noFormat`const data = () => {};`, + }, + { + code: 'function data() {}', + errors: [ + { + data: { constraint: 'any' }, + messageId: 'unnecessaryConstraint', + endColumn: 37, + column: 15, + line: 1, + }, + ], + output: 'function data() {}', + }, + { + code: 'function data() {}', + errors: [ + { + data: { constraint: 'unknown' }, + messageId: 'unnecessaryConstraint', + endColumn: 32, + column: 15, + line: 1, + }, + ], + output: 'function data() {}', + }, + { + code: 'function data() {}', + errors: [ + { + data: { constraint: 'unknown' }, + messageId: 'unnecessaryConstraint', + endColumn: 41, + column: 15, + line: 1, + }, + ], + output: 'function data() {}', + }, + { + code: 'const data = () => {};', + errors: [ + { + data: { constraint: 'any' }, + messageId: 'unnecessaryConstraint', + endColumn: 28, + column: 15, + line: 1, + }, + ], + output: 'const data = () => {};', + }, + { + code: 'const data = () => {};', + errors: [ + { + data: { constraint: 'any' }, + messageId: 'unnecessaryConstraint', + endColumn: 37, + column: 15, + line: 1, + }, + ], + output: 'const data = () => {};', + }, + { + code: 'const data = () => {};', + errors: [ + { + data: { constraint: 'unknown' }, + messageId: 'unnecessaryConstraint', + endColumn: 32, + column: 15, + line: 1, + }, + ], + output: 'const data = () => {};', + }, + { + code: 'const data = () => {};', + errors: [ + { + data: { constraint: 'unknown' }, + messageId: 'unnecessaryConstraint', + endColumn: 41, + column: 15, + line: 1, + }, + ], + output: 'const data = () => {};', + }, + { + code: 'class Data {}', + errors: [ + { + data: { constraint: 'unknown' }, + messageId: 'unnecessaryConstraint', + endColumn: 29, + column: 12, + line: 1, + }, + ], + output: 'class Data {}', + }, + { + code: 'const Data = class {};', + errors: [ + { + data: { constraint: 'unknown' }, + messageId: 'unnecessaryConstraint', + endColumn: 37, + column: 20, + line: 1, + }, + ], + output: 'const Data = class {};', + }, + { + code: ` +class Data { + member() {} +} + `, + errors: [ + { + data: { constraint: 'unknown' }, + messageId: 'unnecessaryConstraint', + endColumn: 27, + column: 10, + line: 3, + }, + ], + output: ` +class Data { + member() {} +} + `, + }, + { + code: ` +const Data = class { + member() {} +}; + `, + errors: [ + { + data: { constraint: 'unknown' }, + messageId: 'unnecessaryConstraint', + endColumn: 27, + column: 10, + line: 3, + }, + ], + output: ` +const Data = class { + member() {} +}; + `, + }, + { + code: 'interface Data {}', + errors: [ + { + data: { constraint: 'unknown' }, + messageId: 'unnecessaryConstraint', + endColumn: 33, + column: 16, + line: 1, + }, + ], + output: 'interface Data {}', + }, + { + code: 'type Data = {};', + errors: [ + { + data: { constraint: 'unknown' }, + messageId: 'unnecessaryConstraint', + endColumn: 28, + column: 11, + line: 1, + }, + ], + output: 'type Data = {};', + }, + ], +}); From 06a192a5733762568873d85327a82920474a5371 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Thu, 1 Oct 2020 23:55:37 -0400 Subject: [PATCH 2/4] Inlined report --- .../rules/no-unnecessary-type-constraint.ts | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-type-constraint.ts b/packages/eslint-plugin/src/rules/no-unnecessary-type-constraint.ts index 4f98a438a66..f393f9247c5 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-constraint.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-constraint.ts @@ -65,24 +65,6 @@ export default util.createRule({ const inJsx = context.getFilename().toLowerCase().endsWith('tsx'); - const report = ( - node: TypeParameterWithConstraint, - constraint: string, - inArrowFunction: boolean, - ): void => { - context.report({ - data: { constraint }, - fix(fixer) { - return fixer.replaceTextRange( - [node.name.range[1], node.constraint.range[1]], - inArrowFunction && inJsx ? ',' : '', - ); - }, - messageId: 'unnecessaryConstraint', - node, - }); - }; - const checkNode = ( node: TypeParameterWithConstraint, inArrowFunction: boolean, @@ -94,7 +76,17 @@ export default util.createRule({ for (const [filter, type] of keywordFilters) { if (filter(constraintType)) { - report(node, type, inArrowFunction); + context.report({ + data: { constraint: type }, + fix(fixer) { + return fixer.replaceTextRange( + [node.name.range[1], node.constraint.range[1]], + inArrowFunction && inJsx ? ',' : '', + ); + }, + messageId: 'unnecessaryConstraint', + node, + }); return; } } From 5ac4a070eea31473ece853f763e2e3fb3a36dfad Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sun, 11 Oct 2020 14:15:06 -0400 Subject: [PATCH 3/4] Improved message and removed type checker --- packages/eslint-plugin/README.md | 2 +- .../rules/no-unnecessary-type-constraint.ts | 69 +++++++-------- .../no-unnecessary-type-constraint.test.ts | 85 +++++-------------- 3 files changed, 52 insertions(+), 104 deletions(-) diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 2cebef01445..5c4f7a8d2f4 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -142,7 +142,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int | [`@typescript-eslint/no-unnecessary-qualifier`](./docs/rules/no-unnecessary-qualifier.md) | Warns when a namespace qualifier is unnecessary | | :wrench: | :thought_balloon: | | [`@typescript-eslint/no-unnecessary-type-arguments`](./docs/rules/no-unnecessary-type-arguments.md) | Enforces that type arguments will not be used if not required | | :wrench: | :thought_balloon: | | [`@typescript-eslint/no-unnecessary-type-assertion`](./docs/rules/no-unnecessary-type-assertion.md) | Warns if a type assertion does not change the type of an expression | :heavy_check_mark: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/no-unnecessary-type-constraint`](./docs/rules/no-unnecessary-type-constraint.md) | Disallows unnecessary constraints on generic types | | :wrench: | :thought_balloon: | +| [`@typescript-eslint/no-unnecessary-type-constraint`](./docs/rules/no-unnecessary-type-constraint.md) | Disallows unnecessary constraints on generic types | | :wrench: | | | [`@typescript-eslint/no-unsafe-assignment`](./docs/rules/no-unsafe-assignment.md) | Disallows assigning any to variables and properties | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/no-unsafe-call`](./docs/rules/no-unsafe-call.md) | Disallows calling an any type value | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/no-unsafe-member-access`](./docs/rules/no-unsafe-member-access.md) | Disallows member access on any typed variables | :heavy_check_mark: | | :thought_balloon: | diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-type-constraint.ts b/packages/eslint-plugin/src/rules/no-unnecessary-type-constraint.ts index f393f9247c5..6d5036c2d75 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-constraint.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-constraint.ts @@ -1,4 +1,7 @@ -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { + AST_NODE_TYPES, + TSESTree, +} from '@typescript-eslint/experimental-utils'; import * as semver from 'semver'; import * as ts from 'typescript'; import * as util from '../util'; @@ -11,8 +14,6 @@ type TypeParameterWithConstraint = MakeRequired< 'constraint' >; -type KeywordFilter = (type: ts.Type) => boolean; - const is3dot5 = semver.satisfies( ts.version, `>= 3.5.0 || >= 3.5.1-rc || >= 3.5.0-beta`, @@ -34,61 +35,55 @@ export default util.createRule({ category: 'Best Practices', description: 'Disallows unnecessary constraints on generic types', recommended: false, - requiresTypeChecking: true, suggestion: true, }, fixable: 'code', messages: { unnecessaryConstraint: - 'Constraining a generic type to {{constraint}} does nothing and is unnecessary.', + 'Constraining the generic type `{{name}}` to `{{constraint}}` does nothing and is unnecessary.', }, schema: [], type: 'suggestion', }, defaultOptions: [], create(context) { - const parserServices = util.getParserServices(context); - const checker = parserServices.program.getTypeChecker(); - - const keywordFilters: [KeywordFilter, string][] = []; - if (is3dot5) { - keywordFilters.push([util.isTypeUnknownType, 'unknown']); - - if (is3dot9) { - keywordFilters.push([util.isTypeAnyType, 'any']); - } - } - - if (!keywordFilters.length) { + if (!is3dot5) { return {}; } + // In theory, we could use the type checker for more advanced constraint types... + // ...but in practice, these types are rare, and likely not worth requiring type info. + // https://github.com/typescript-eslint/typescript-eslint/pull/2516#discussion_r495731858 + const unnecessaryConstraints = is3dot9 + ? new Map([ + [AST_NODE_TYPES.TSAnyKeyword, 'any'], + [AST_NODE_TYPES.TSUnknownKeyword, 'unknown'], + ]) + : new Map([[AST_NODE_TYPES.TSUnknownKeyword, 'unknown']]); + const inJsx = context.getFilename().toLowerCase().endsWith('tsx'); const checkNode = ( node: TypeParameterWithConstraint, inArrowFunction: boolean, ): void => { - const constraint = parserServices.esTreeNodeToTSNodeMap.get( - node.constraint, - ); - const constraintType = checker.getTypeAtLocation(constraint); + const constraint = unnecessaryConstraints.get(node.constraint.type); - for (const [filter, type] of keywordFilters) { - if (filter(constraintType)) { - context.report({ - data: { constraint: type }, - fix(fixer) { - return fixer.replaceTextRange( - [node.name.range[1], node.constraint.range[1]], - inArrowFunction && inJsx ? ',' : '', - ); - }, - messageId: 'unnecessaryConstraint', - node, - }); - return; - } + if (constraint) { + context.report({ + data: { + constraint, + name: node.name.name, + }, + fix(fixer) { + return fixer.replaceTextRange( + [node.name.range[1], node.constraint.range[1]], + inArrowFunction && inJsx ? ',' : '', + ); + }, + messageId: 'unnecessaryConstraint', + node, + }); } }; diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts index 6a6a4b2b3d8..57bc54f9cd6 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts @@ -19,6 +19,11 @@ ruleTester.run('no-unnecessary-type-constraint', rule, { 'function data() {}', 'function data() {}', 'function data() {}', + 'function data() {}', + ` +type TODO = any; +function data() {} + `, 'const data = () => {};', 'const data = () => {};', 'const data = () => {};', @@ -30,7 +35,7 @@ ruleTester.run('no-unnecessary-type-constraint', rule, { code: 'function data() {}', errors: [ { - data: { constraint: 'any' }, + data: { constraint: 'any', name: 'T' }, messageId: 'unnecessaryConstraint', endColumn: 28, column: 15, @@ -43,7 +48,7 @@ ruleTester.run('no-unnecessary-type-constraint', rule, { code: 'function data() {}', errors: [ { - data: { constraint: 'any' }, + data: { constraint: 'any', name: 'T' }, messageId: 'unnecessaryConstraint', endColumn: 28, column: 15, @@ -56,7 +61,7 @@ ruleTester.run('no-unnecessary-type-constraint', rule, { code: 'function data() {}', errors: [ { - data: { constraint: 'any' }, + data: { constraint: 'any', name: 'U' }, messageId: 'unnecessaryConstraint', endColumn: 31, column: 18, @@ -69,7 +74,7 @@ ruleTester.run('no-unnecessary-type-constraint', rule, { code: 'function data() {}', errors: [ { - data: { constraint: 'any' }, + data: { constraint: 'any', name: 'T' }, messageId: 'unnecessaryConstraint', endColumn: 28, column: 15, @@ -82,7 +87,7 @@ ruleTester.run('no-unnecessary-type-constraint', rule, { code: 'const data = () => {};', errors: [ { - data: { constraint: 'any' }, + data: { constraint: 'any', name: 'T' }, messageId: 'unnecessaryConstraint', endColumn: 28, column: 15, @@ -92,24 +97,11 @@ ruleTester.run('no-unnecessary-type-constraint', rule, { filename: 'react.tsx', output: noFormat`const data = () => {};`, }, - { - code: 'function data() {}', - errors: [ - { - data: { constraint: 'any' }, - messageId: 'unnecessaryConstraint', - endColumn: 37, - column: 15, - line: 1, - }, - ], - output: 'function data() {}', - }, { code: 'function data() {}', errors: [ { - data: { constraint: 'unknown' }, + data: { constraint: 'unknown', name: 'T' }, messageId: 'unnecessaryConstraint', endColumn: 32, column: 15, @@ -118,24 +110,11 @@ ruleTester.run('no-unnecessary-type-constraint', rule, { ], output: 'function data() {}', }, - { - code: 'function data() {}', - errors: [ - { - data: { constraint: 'unknown' }, - messageId: 'unnecessaryConstraint', - endColumn: 41, - column: 15, - line: 1, - }, - ], - output: 'function data() {}', - }, { code: 'const data = () => {};', errors: [ { - data: { constraint: 'any' }, + data: { constraint: 'any', name: 'T' }, messageId: 'unnecessaryConstraint', endColumn: 28, column: 15, @@ -144,24 +123,11 @@ ruleTester.run('no-unnecessary-type-constraint', rule, { ], output: 'const data = () => {};', }, - { - code: 'const data = () => {};', - errors: [ - { - data: { constraint: 'any' }, - messageId: 'unnecessaryConstraint', - endColumn: 37, - column: 15, - line: 1, - }, - ], - output: 'const data = () => {};', - }, { code: 'const data = () => {};', errors: [ { - data: { constraint: 'unknown' }, + data: { constraint: 'unknown', name: 'T' }, messageId: 'unnecessaryConstraint', endColumn: 32, column: 15, @@ -170,24 +136,11 @@ ruleTester.run('no-unnecessary-type-constraint', rule, { ], output: 'const data = () => {};', }, - { - code: 'const data = () => {};', - errors: [ - { - data: { constraint: 'unknown' }, - messageId: 'unnecessaryConstraint', - endColumn: 41, - column: 15, - line: 1, - }, - ], - output: 'const data = () => {};', - }, { code: 'class Data {}', errors: [ { - data: { constraint: 'unknown' }, + data: { constraint: 'unknown', name: 'T' }, messageId: 'unnecessaryConstraint', endColumn: 29, column: 12, @@ -200,7 +153,7 @@ ruleTester.run('no-unnecessary-type-constraint', rule, { code: 'const Data = class {};', errors: [ { - data: { constraint: 'unknown' }, + data: { constraint: 'unknown', name: 'T' }, messageId: 'unnecessaryConstraint', endColumn: 37, column: 20, @@ -217,7 +170,7 @@ class Data { `, errors: [ { - data: { constraint: 'unknown' }, + data: { constraint: 'unknown', name: 'T' }, messageId: 'unnecessaryConstraint', endColumn: 27, column: 10, @@ -238,7 +191,7 @@ const Data = class { `, errors: [ { - data: { constraint: 'unknown' }, + data: { constraint: 'unknown', name: 'T' }, messageId: 'unnecessaryConstraint', endColumn: 27, column: 10, @@ -255,7 +208,7 @@ const Data = class { code: 'interface Data {}', errors: [ { - data: { constraint: 'unknown' }, + data: { constraint: 'unknown', name: 'T' }, messageId: 'unnecessaryConstraint', endColumn: 33, column: 16, @@ -268,7 +221,7 @@ const Data = class { code: 'type Data = {};', errors: [ { - data: { constraint: 'unknown' }, + data: { constraint: 'unknown', name: 'T' }, messageId: 'unnecessaryConstraint', endColumn: 28, column: 11, From 241e15d369e945ab242ac6821813068a8c573bbd Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sun, 25 Oct 2020 22:51:25 -0400 Subject: [PATCH 4/4] Stop testing with type info --- .../tests/rules/no-unnecessary-type-constraint.test.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts index 57bc54f9cd6..c71821ffeb4 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts @@ -1,13 +1,9 @@ import rule from '../../src/rules/no-unnecessary-type-constraint'; -import { RuleTester, getFixturesRootDir, noFormat } from '../RuleTester'; - -const rootDir = getFixturesRootDir(); +import { RuleTester, noFormat } from '../RuleTester'; const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2018, - tsconfigRootDir: rootDir, - project: './tsconfig.json', }, parser: '@typescript-eslint/parser', });