From f8529f3ab30186deecd97e79ec3abb83e662c6fa Mon Sep 17 00:00:00 2001 From: Benjamin Lichtman Date: Tue, 5 Feb 2019 23:43:15 -0800 Subject: [PATCH 1/4] feat(eslint-plugin): add callable-types rule --- packages/eslint-plugin/README.md | 1 + packages/eslint-plugin/ROADMAP.md | 7 +- .../docs/rules/callable-types.md | 57 ++++++ .../eslint-plugin/lib/rules/callable-types.js | 177 ++++++++++++++++++ .../tests/lib/rules/callable-types.js | 147 +++++++++++++++ 5 files changed, 386 insertions(+), 3 deletions(-) create mode 100644 packages/eslint-plugin/docs/rules/callable-types.md create mode 100644 packages/eslint-plugin/lib/rules/callable-types.js create mode 100644 packages/eslint-plugin/tests/lib/rules/callable-types.js diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index cacb6666fddf..5fce6e91ac16 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -113,6 +113,7 @@ Then you should add `airbnb` (or `airbnb-base`) to your `extends` section of `.e | [`@typescript-eslint/adjacent-overload-signatures`](./docs/rules/adjacent-overload-signatures.md) | Require that member overloads be consecutive (`adjacent-overload-signatures` from TSLint) | :heavy_check_mark: | | | [`@typescript-eslint/array-type`](./docs/rules/array-type.md) | Requires using either `T[]` or `Array` for arrays (`array-type` from TSLint) | :heavy_check_mark: | :wrench: | | [`@typescript-eslint/ban-types`](./docs/rules/ban-types.md) | Enforces that types will not to be used (`ban-types` from TSLint) | :heavy_check_mark: | :wrench: | +| [`@typescript-eslint/callable-types`](./docs/rules/callable-types.md) | Use function types instead of interfaces with call signatures (`callable-types` from TSLint) | | :wrench: | | [`@typescript-eslint/camelcase`](./docs/rules/camelcase.md) | Enforce camelCase naming convention | :heavy_check_mark: | | | [`@typescript-eslint/class-name-casing`](./docs/rules/class-name-casing.md) | Require PascalCased class and interface names (`class-name` from TSLint) | :heavy_check_mark: | | | [`@typescript-eslint/explicit-function-return-type`](./docs/rules/explicit-function-return-type.md) | Require explicit return types on functions and class methods | :heavy_check_mark: | | diff --git a/packages/eslint-plugin/ROADMAP.md b/packages/eslint-plugin/ROADMAP.md index bd0480101cf1..9677a93a135d 100644 --- a/packages/eslint-plugin/ROADMAP.md +++ b/packages/eslint-plugin/ROADMAP.md @@ -1,10 +1,10 @@ # Roadmap -✅ (29) = done
+✅ (30) = done
🌟 (79) = in ESLint core
🔌 (33) = in another plugin
🌓 (16) = implementations differ or ESLint version is missing functionality
-🛑 (68) = unimplemented +🛑 (67) = unimplemented ## TSLint rules @@ -132,7 +132,7 @@ | [`arrow-parens`] | 🌟 | [`arrow-parens`][arrow-parens] | | [`arrow-return-shorthand`] | 🌟 | [`arrow-body-style`][arrow-body-style] | | [`binary-expression-operand-order`] | 🌟 | [`yoda`][yoda] | -| [`callable-types`] | 🛑 | N/A | +| [`callable-types`] | ✅ | [`@typescript-eslint/callable-types`] | | [`class-name`] | ✅ | [`@typescript-eslint/class-name-casing`] | | [`comment-format`] | 🌟 | [`capitalized-comments`][capitalized-comments] & [`spaced-comment`][spaced-comment] | | [`completed-docs`] | 🔌 | [`eslint-plugin-jsdoc`][plugin:jsdoc] | @@ -586,6 +586,7 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint- [`@typescript-eslint/member-delimiter-style`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/member-delimiter-style.md [`@typescript-eslint/prefer-interface`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-interface.md [`@typescript-eslint/no-array-constructor`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-array-constructor.md +[`@typescript-eslint/callable-types`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/callable-types.md diff --git a/packages/eslint-plugin/docs/rules/callable-types.md b/packages/eslint-plugin/docs/rules/callable-types.md new file mode 100644 index 000000000000..d72708b1bb05 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/callable-types.md @@ -0,0 +1,57 @@ +# Use function types instead of interfaces with call signatures (callable-types) + +## Rule Details + +This rule suggests using a function type instead of an interface or object type literal with a single call signature. + +Examples of **incorrect** code for this rule: + +```ts +interface Foo { + (): string; +} +``` + +```ts +function foo(bar: { (): number }): number { + return bar(); +} +``` + +```ts +interface Foo extends Function { + (): void; +} +``` + +Examples of **correct** code for this rule: + +```ts +interface Foo { + (): void; + bar: number; +} +``` + +```ts +function foo(bar: { (): string; baz: number }): string { + return bar(); +} +``` + +```ts +interface Foo { + bar: string; +} +interface Bar extends Foo { + (): void; +} +``` + +## When Not To Use It + +If you specifically want to use an interface or type literal with a single call signature for stylistic reasons, you can disable this rule. + +## Further Reading + +- TSLint: [`callable-types`](https://palantir.github.io/tslint/rules/callable-types/) diff --git a/packages/eslint-plugin/lib/rules/callable-types.js b/packages/eslint-plugin/lib/rules/callable-types.js new file mode 100644 index 000000000000..84bfd7cd25a2 --- /dev/null +++ b/packages/eslint-plugin/lib/rules/callable-types.js @@ -0,0 +1,177 @@ +/** + * @fileoverview Use function types instead of interfaces with call signatures + * @author Benjamin Lichtman + */ +'use strict'; +const util = require('../util'); + +/** + * @typedef {import("eslint").Rule.RuleModule} RuleModule + * @typedef {import("estree").Node} ESTreeNode + */ + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +/** + * @type {RuleModule} + */ +module.exports = { + meta: { + docs: { + description: + 'Use function types instead of interfaces with call signatures', + category: 'TypeScript', + recommended: false, + extraDescription: [util.tslintRule('callable-types')], + url: util.metaDocsUrl('callable-types') + }, + fixable: 'code', + messages: { + callableTypeViolation: + "{{ type }} has only a call signature - use '{{ sigSuggestion }}' instead." + }, + schema: [], + type: 'suggestion' + }, + + create(context) { + const sourceCode = context.getSourceCode(); + + //---------------------------------------------------------------------- + // Helpers + //---------------------------------------------------------------------- + + /** + * Checks if there is no supertype or if the supertype is 'Function' + * @param {ESTreeNode} node The node being checked + * @returns {boolean} Returns true iff there is no supertype or if the supertype is 'Function' + */ + function noSupertype(node) { + if ( + typeof node.extends === 'undefined' && + typeof node.implements === 'undefined' + ) { + return true; + } + const heritageClauses = node.extends.concat(node.implements || []); + if (heritageClauses.length !== 1) { + return false; + } + const expr = heritageClauses[0].expression; + + return expr.type === 'Identifier' && expr.name === 'Function'; + } + + /** + * @param {ESTreeNode} parent The parent of the call signature causing the diagnostic + * @returns {boolean} true iff the parent node needs to be wrapped for readability + */ + function shouldWrapSuggestion(parent) { + switch (parent.type) { + case 'TSUnionType': + case 'TSIntersectionType': + case 'TSArrayType': + return true; + default: + return false; + } + } + + /** + * @param {ESTreeNode} call The call signature causing the diagnostic + * @param {ESTreeNode} parent The parent of the call + * @returns {string} The suggestion to report + */ + function renderSuggestion(call, parent) { + const start = call.range[0]; + const colonPos = call.returnType.range[0] - start; + const text = sourceCode.getText().slice(start, call.range[1]); + + let suggestion = `${text.slice(0, colonPos)} =>${text.slice( + colonPos + 1 + )}`; + + if (shouldWrapSuggestion(parent.parent)) { + suggestion = `(${suggestion})`; + } + if (parent.type === 'TSInterfaceDeclaration') { + if (typeof parent.typeParameters !== 'undefined') { + return `type ${sourceCode + .getText() + .slice( + parent.id.range[0], + parent.typeParameters.range[1] + )} = ${suggestion}`; + } + return `type ${parent.id.name} = ${suggestion}`; + } + return suggestion.endsWith(';') ? suggestion.slice(0, -1) : suggestion; + } + + /** + * @param {ESTreeNode} member The TypeElement being checked + * @param {ESTreeNode} node The parent of member being checked + * @returns {void} + */ + function checkMember(member, node) { + if ( + (member.type === 'TSCallSignatureDeclaration' || + member.type === 'TSConstructSignatureDeclaration') && + typeof member.returnType !== 'undefined' + ) { + const suggestion = renderSuggestion(member, node); + const fixStart = + node.type === 'TSTypeLiteral' + ? node.range[0] + : sourceCode + .getTokens(node) + .filter( + token => + token.type === 'Keyword' && token.value === 'interface' + )[0].range[0]; + + context.report({ + node: member, + messageId: 'callableTypeViolation', + data: { + type: node.type === 'TSTypeLiteral' ? 'Type literal' : 'Interface', + sigSuggestion: suggestion + }, + fix(fixer) { + return fixer.replaceTextRange( + [fixStart, node.range[1]], + suggestion + ); + } + }); + } + } + + //---------------------------------------------------------------------- + // Public + //---------------------------------------------------------------------- + + return { + /** + * @param {TSInterfaceDeclaration} node The node being checked + * @returns {void} + */ + TSInterfaceDeclaration(node) { + if (noSupertype(node) && node.body.body.length === 1) { + checkMember(node.body.body[0], node); + } + }, + /** + * @param {TSTypeLiteral} node The node being checked + * @returns {void} + */ + TSTypeLiteral(node) { + if (node.members.length === 1) { + checkMember(node.members[0], node); + } + } + }; + } +}; diff --git a/packages/eslint-plugin/tests/lib/rules/callable-types.js b/packages/eslint-plugin/tests/lib/rules/callable-types.js new file mode 100644 index 000000000000..071655ae4d97 --- /dev/null +++ b/packages/eslint-plugin/tests/lib/rules/callable-types.js @@ -0,0 +1,147 @@ +/** + * @fileoverview Use function types instead of interfaces with call signatures + * @author Benjamin Lichtman + */ +'use strict'; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +var rule = require('../../../lib/rules/callable-types'), + RuleTester = require('eslint').RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const parserOptions = { + ecmaVersion: 2015 +}; +var ruleTester = new RuleTester({ + parserOptions, + parser: '@typescript-eslint/parser' +}); +ruleTester.run('callable-types', rule, { + valid: [ + ` +interface Foo { + (): void; + bar: number; +}`, + ` +type Foo = { + (): void; + bar: number; +}`, + ` +function foo(bar: { (): string, baz: number }): string { + return bar(); +}`, + ` +interface Foo { + bar: string; +} +interface Bar extends Foo { + (): void; +}`, + ` +interface Foo { + bar: string; +} +interface Bar extends Function, Foo { + (): void; +}` + ], + + invalid: [ + { + code: ` +interface Foo { + (): string; +}`, + errors: [ + { + messageId: 'callableTypeViolation', + type: 'TSCallSignatureDeclaration' + } + ], + output: ` +type Foo = () => string;` + }, + { + code: ` +type Foo = { + (): string; +}`, + errors: [ + { + messageId: 'callableTypeViolation', + type: 'TSCallSignatureDeclaration' + } + ], + output: ` +type Foo = () => string` + }, + { + code: ` +function foo(bar: { (s: string): number }): number { + return bar("hello"); +}`, + errors: [ + { + messageId: 'callableTypeViolation', + type: 'TSCallSignatureDeclaration' + } + ], + output: ` +function foo(bar: (s: string) => number): number { + return bar("hello"); +}` + }, + { + code: ` +function foo(bar: { (s: string): number } | undefined): number { + return bar("hello"); +}`, + errors: [ + { + messageId: 'callableTypeViolation', + type: 'TSCallSignatureDeclaration' + } + ], + output: ` +function foo(bar: ((s: string) => number) | undefined): number { + return bar("hello"); +}` + }, + { + code: ` +interface Foo extends Function { + (): void; +}`, + errors: [ + { + messageId: 'callableTypeViolation', + type: 'TSCallSignatureDeclaration' + } + ], + output: ` +type Foo = () => void;` + }, + { + code: ` +interface Foo { + (bar: T): string; +}`, + errors: [ + { + messageId: 'callableTypeViolation', + type: 'TSCallSignatureDeclaration' + } + ], + output: ` +type Foo = (bar: T) => string;` + } + ] +}); From a0411b6a0d75f885f92a15c604786c27666658bb Mon Sep 17 00:00:00 2001 From: Benjamin Lichtman Date: Wed, 6 Feb 2019 13:52:22 -0800 Subject: [PATCH 2/4] fix: refine selector and ignore implements --- .../eslint-plugin/lib/rules/callable-types.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/packages/eslint-plugin/lib/rules/callable-types.js b/packages/eslint-plugin/lib/rules/callable-types.js index 84bfd7cd25a2..4f57f6ce7d4d 100644 --- a/packages/eslint-plugin/lib/rules/callable-types.js +++ b/packages/eslint-plugin/lib/rules/callable-types.js @@ -49,17 +49,13 @@ module.exports = { * @returns {boolean} Returns true iff there is no supertype or if the supertype is 'Function' */ function noSupertype(node) { - if ( - typeof node.extends === 'undefined' && - typeof node.implements === 'undefined' - ) { + if (!node.extends || node.extends.length === 0) { return true; } - const heritageClauses = node.extends.concat(node.implements || []); - if (heritageClauses.length !== 1) { + if (node.extends.length !== 1) { return false; } - const expr = heritageClauses[0].expression; + const expr = node.extends[0].expression; return expr.type === 'Identifier' && expr.name === 'Function'; } @@ -167,10 +163,8 @@ module.exports = { * @param {TSTypeLiteral} node The node being checked * @returns {void} */ - TSTypeLiteral(node) { - if (node.members.length === 1) { - checkMember(node.members[0], node); - } + 'TSTypeLiteral[members.length = 1]'(node) { + checkMember(node.members[0], node); } }; } From 65f56b82f0fb345fea901fa6adcf63776ba2cf3f Mon Sep 17 00:00:00 2001 From: Benjamin Lichtman Date: Wed, 6 Feb 2019 19:28:03 -0800 Subject: [PATCH 3/4] fix: change rule name --- packages/eslint-plugin/README.md | 2 +- packages/eslint-plugin/ROADMAP.md | 4 ++-- .../docs/rules/{callable-types.md => prefer-function-type.md} | 2 +- .../lib/rules/{callable-types.js => prefer-function-type.js} | 4 ++-- .../lib/rules/{callable-types.js => prefer-function-type.js} | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) rename packages/eslint-plugin/docs/rules/{callable-types.md => prefer-function-type.md} (97%) rename packages/eslint-plugin/lib/rules/{callable-types.js => prefer-function-type.js} (97%) rename packages/eslint-plugin/tests/lib/rules/{callable-types.js => prefer-function-type.js} (96%) diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 5fce6e91ac16..10899ec8533c 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -113,7 +113,6 @@ Then you should add `airbnb` (or `airbnb-base`) to your `extends` section of `.e | [`@typescript-eslint/adjacent-overload-signatures`](./docs/rules/adjacent-overload-signatures.md) | Require that member overloads be consecutive (`adjacent-overload-signatures` from TSLint) | :heavy_check_mark: | | | [`@typescript-eslint/array-type`](./docs/rules/array-type.md) | Requires using either `T[]` or `Array` for arrays (`array-type` from TSLint) | :heavy_check_mark: | :wrench: | | [`@typescript-eslint/ban-types`](./docs/rules/ban-types.md) | Enforces that types will not to be used (`ban-types` from TSLint) | :heavy_check_mark: | :wrench: | -| [`@typescript-eslint/callable-types`](./docs/rules/callable-types.md) | Use function types instead of interfaces with call signatures (`callable-types` from TSLint) | | :wrench: | | [`@typescript-eslint/camelcase`](./docs/rules/camelcase.md) | Enforce camelCase naming convention | :heavy_check_mark: | | | [`@typescript-eslint/class-name-casing`](./docs/rules/class-name-casing.md) | Require PascalCased class and interface names (`class-name` from TSLint) | :heavy_check_mark: | | | [`@typescript-eslint/explicit-function-return-type`](./docs/rules/explicit-function-return-type.md) | Require explicit return types on functions and class methods | :heavy_check_mark: | | @@ -143,6 +142,7 @@ Then you should add `airbnb` (or `airbnb-base`) to your `extends` section of `.e | [`@typescript-eslint/no-use-before-define`](./docs/rules/no-use-before-define.md) | Disallow the use of variables before they are defined | :heavy_check_mark: | | | [`@typescript-eslint/no-useless-constructor`](./docs/rules/no-useless-constructor.md) | Disallow unnecessary constructors | | | | [`@typescript-eslint/no-var-requires`](./docs/rules/no-var-requires.md) | Disallows the use of require statements except in import statements (`no-var-requires` from TSLint) | :heavy_check_mark: | | +| [`@typescript-eslint/prefer-function-type`](./docs/rules/prefer-function-type.md) | Use function types instead of interfaces with call signatures (`prefer-function-type` from TSLint) | | :wrench: | | [`@typescript-eslint/prefer-interface`](./docs/rules/prefer-interface.md) | Prefer an interface declaration over a type literal (type T = { ... }) (`interface-over-type-literal` from TSLint) | :heavy_check_mark: | :wrench: | | [`@typescript-eslint/prefer-namespace-keyword`](./docs/rules/prefer-namespace-keyword.md) | Require the use of the `namespace` keyword instead of the `module` keyword to declare custom TypeScript modules. (`no-internal-module` from TSLint) | :heavy_check_mark: | :wrench: | | [`@typescript-eslint/restrict-plus-operands`](./docs/rules/restrict-plus-operands.md) | When adding two variables, operands must both be of type number or of type string. (`restrict-plus-operands` from TSLint) | | | diff --git a/packages/eslint-plugin/ROADMAP.md b/packages/eslint-plugin/ROADMAP.md index 9677a93a135d..d26d61721ce3 100644 --- a/packages/eslint-plugin/ROADMAP.md +++ b/packages/eslint-plugin/ROADMAP.md @@ -132,7 +132,7 @@ | [`arrow-parens`] | 🌟 | [`arrow-parens`][arrow-parens] | | [`arrow-return-shorthand`] | 🌟 | [`arrow-body-style`][arrow-body-style] | | [`binary-expression-operand-order`] | 🌟 | [`yoda`][yoda] | -| [`callable-types`] | ✅ | [`@typescript-eslint/callable-types`] | +| [`callable-types`] | ✅ | [`@typescript-eslint/prefer-function-type`] | | [`class-name`] | ✅ | [`@typescript-eslint/class-name-casing`] | | [`comment-format`] | 🌟 | [`capitalized-comments`][capitalized-comments] & [`spaced-comment`][spaced-comment] | | [`completed-docs`] | 🔌 | [`eslint-plugin-jsdoc`][plugin:jsdoc] | @@ -586,7 +586,7 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint- [`@typescript-eslint/member-delimiter-style`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/member-delimiter-style.md [`@typescript-eslint/prefer-interface`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-interface.md [`@typescript-eslint/no-array-constructor`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-array-constructor.md -[`@typescript-eslint/callable-types`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/callable-types.md +[`@typescript-eslint/prefer-function-type`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-function-type.md diff --git a/packages/eslint-plugin/docs/rules/callable-types.md b/packages/eslint-plugin/docs/rules/prefer-function-type.md similarity index 97% rename from packages/eslint-plugin/docs/rules/callable-types.md rename to packages/eslint-plugin/docs/rules/prefer-function-type.md index d72708b1bb05..7cbf2be0480d 100644 --- a/packages/eslint-plugin/docs/rules/callable-types.md +++ b/packages/eslint-plugin/docs/rules/prefer-function-type.md @@ -1,4 +1,4 @@ -# Use function types instead of interfaces with call signatures (callable-types) +# Use function types instead of interfaces with call signatures (prefer-function-type) ## Rule Details diff --git a/packages/eslint-plugin/lib/rules/callable-types.js b/packages/eslint-plugin/lib/rules/prefer-function-type.js similarity index 97% rename from packages/eslint-plugin/lib/rules/callable-types.js rename to packages/eslint-plugin/lib/rules/prefer-function-type.js index 4f57f6ce7d4d..168c163cfc84 100644 --- a/packages/eslint-plugin/lib/rules/callable-types.js +++ b/packages/eslint-plugin/lib/rules/prefer-function-type.js @@ -24,8 +24,8 @@ module.exports = { 'Use function types instead of interfaces with call signatures', category: 'TypeScript', recommended: false, - extraDescription: [util.tslintRule('callable-types')], - url: util.metaDocsUrl('callable-types') + extraDescription: [util.tslintRule('prefer-function-type')], + url: util.metaDocsUrl('prefer-function-type') }, fixable: 'code', messages: { diff --git a/packages/eslint-plugin/tests/lib/rules/callable-types.js b/packages/eslint-plugin/tests/lib/rules/prefer-function-type.js similarity index 96% rename from packages/eslint-plugin/tests/lib/rules/callable-types.js rename to packages/eslint-plugin/tests/lib/rules/prefer-function-type.js index 071655ae4d97..e13d16ce4497 100644 --- a/packages/eslint-plugin/tests/lib/rules/callable-types.js +++ b/packages/eslint-plugin/tests/lib/rules/prefer-function-type.js @@ -8,7 +8,7 @@ // Requirements //------------------------------------------------------------------------------ -var rule = require('../../../lib/rules/callable-types'), +var rule = require('../../../lib/rules/prefer-function-type'), RuleTester = require('eslint').RuleTester; //------------------------------------------------------------------------------ @@ -22,7 +22,7 @@ var ruleTester = new RuleTester({ parserOptions, parser: '@typescript-eslint/parser' }); -ruleTester.run('callable-types', rule, { +ruleTester.run('prefer-function-type', rule, { valid: [ ` interface Foo { From 958b75ef7f164c8e807802c29bceed93a394499b Mon Sep 17 00:00:00 2001 From: Benjamin Lichtman Date: Thu, 7 Feb 2019 16:43:17 -0800 Subject: [PATCH 4/4] fix: update rule name change --- packages/eslint-plugin/README.md | 2 +- .../eslint-plugin/lib/rules/prefer-function-type.js | 4 ++-- .../tests/lib/rules/prefer-function-type.js | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index bd14acca9fd7..7aff2761aca8 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -143,7 +143,7 @@ Then you should add `airbnb` (or `airbnb-base`) to your `extends` section of `.e | [`@typescript-eslint/no-use-before-define`](./docs/rules/no-use-before-define.md) | Disallow the use of variables before they are defined | :heavy_check_mark: | | | [`@typescript-eslint/no-useless-constructor`](./docs/rules/no-useless-constructor.md) | Disallow unnecessary constructors | | | | [`@typescript-eslint/no-var-requires`](./docs/rules/no-var-requires.md) | Disallows the use of require statements except in import statements (`no-var-requires` from TSLint) | :heavy_check_mark: | | -| [`@typescript-eslint/prefer-function-type`](./docs/rules/prefer-function-type.md) | Use function types instead of interfaces with call signatures (`prefer-function-type` from TSLint) | | :wrench: | +| [`@typescript-eslint/prefer-function-type`](./docs/rules/prefer-function-type.md) | Use function types instead of interfaces with call signatures (`callable-types` from TSLint) | | :wrench: | | [`@typescript-eslint/prefer-interface`](./docs/rules/prefer-interface.md) | Prefer an interface declaration over a type literal (type T = { ... }) (`interface-over-type-literal` from TSLint) | :heavy_check_mark: | :wrench: | | [`@typescript-eslint/prefer-namespace-keyword`](./docs/rules/prefer-namespace-keyword.md) | Require the use of the `namespace` keyword instead of the `module` keyword to declare custom TypeScript modules. (`no-internal-module` from TSLint) | :heavy_check_mark: | :wrench: | | [`@typescript-eslint/restrict-plus-operands`](./docs/rules/restrict-plus-operands.md) | When adding two variables, operands must both be of type number or of type string. (`restrict-plus-operands` from TSLint) | | | diff --git a/packages/eslint-plugin/lib/rules/prefer-function-type.js b/packages/eslint-plugin/lib/rules/prefer-function-type.js index 168c163cfc84..f591179932fb 100644 --- a/packages/eslint-plugin/lib/rules/prefer-function-type.js +++ b/packages/eslint-plugin/lib/rules/prefer-function-type.js @@ -29,7 +29,7 @@ module.exports = { }, fixable: 'code', messages: { - callableTypeViolation: + functionTypeOverCallableType: "{{ type }} has only a call signature - use '{{ sigSuggestion }}' instead." }, schema: [], @@ -130,7 +130,7 @@ module.exports = { context.report({ node: member, - messageId: 'callableTypeViolation', + messageId: 'functionTypeOverCallableType', data: { type: node.type === 'TSTypeLiteral' ? 'Type literal' : 'Interface', sigSuggestion: suggestion diff --git a/packages/eslint-plugin/tests/lib/rules/prefer-function-type.js b/packages/eslint-plugin/tests/lib/rules/prefer-function-type.js index e13d16ce4497..d0587afcaf7c 100644 --- a/packages/eslint-plugin/tests/lib/rules/prefer-function-type.js +++ b/packages/eslint-plugin/tests/lib/rules/prefer-function-type.js @@ -62,7 +62,7 @@ interface Foo { }`, errors: [ { - messageId: 'callableTypeViolation', + messageId: 'functionTypeOverCallableType', type: 'TSCallSignatureDeclaration' } ], @@ -76,7 +76,7 @@ type Foo = { }`, errors: [ { - messageId: 'callableTypeViolation', + messageId: 'functionTypeOverCallableType', type: 'TSCallSignatureDeclaration' } ], @@ -90,7 +90,7 @@ function foo(bar: { (s: string): number }): number { }`, errors: [ { - messageId: 'callableTypeViolation', + messageId: 'functionTypeOverCallableType', type: 'TSCallSignatureDeclaration' } ], @@ -106,7 +106,7 @@ function foo(bar: { (s: string): number } | undefined): number { }`, errors: [ { - messageId: 'callableTypeViolation', + messageId: 'functionTypeOverCallableType', type: 'TSCallSignatureDeclaration' } ], @@ -122,7 +122,7 @@ interface Foo extends Function { }`, errors: [ { - messageId: 'callableTypeViolation', + messageId: 'functionTypeOverCallableType', type: 'TSCallSignatureDeclaration' } ], @@ -136,7 +136,7 @@ interface Foo { }`, errors: [ { - messageId: 'callableTypeViolation', + messageId: 'functionTypeOverCallableType', type: 'TSCallSignatureDeclaration' } ],