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' } ], pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy