From fd75424f56094db9e1b832adc31fa9ab5bc04d31 Mon Sep 17 00:00:00 2001 From: G r e y Date: Sat, 11 Apr 2020 13:05:42 -0500 Subject: [PATCH 01/18] docs(eslint-plugin): fix typos in no-unsafe-assign (#1881) Co-authored-by: Brad Zacher --- packages/eslint-plugin/docs/rules/no-unsafe-assignment.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/no-unsafe-assignment.md b/packages/eslint-plugin/docs/rules/no-unsafe-assignment.md index 5cb6e8d61c2..1a452e431eb 100644 --- a/packages/eslint-plugin/docs/rules/no-unsafe-assignment.md +++ b/packages/eslint-plugin/docs/rules/no-unsafe-assignment.md @@ -1,12 +1,12 @@ # Disallows assigning any to variables and properties (`no-unsafe-assignment`) Despite your best intentions, the `any` type can sometimes leak into your codebase. -Assigning an `any` typed value to a variable can be hard to pick up on, particularly if it leaks in from an external library. Operations on the variable will not checked at all by TypeScript, so it creates a potential safety hole, and source of bugs in your codebase. +Assigning an `any` typed value to a variable can be hard to pick up on, particularly if it leaks in from an external library. Operations on the variable will not be checked at all by TypeScript, so it creates a potential safety hole, and source of bugs in your codebase. ## Rule Details -This rule disallows the assigning `any` to a variable, and assigning `any[]` to an array destructuring. -This rule also compares the assigned type to the variable's declared/inferred return type to ensure you don't return an unsafe `any` in a generic position to a receiver that's expecting a specific type. For example, it will error if you return `Set` from a function declared as returning `Set`. +This rule disallows assigning `any` to a variable, and assigning `any[]` to an array destructuring. +This rule also compares the assigned type to the variable's type to ensure you don't assign an unsafe `any` in a generic position to a receiver that's expecting a specific type. For example, it will error if you assign `Set` to a variable declared as `Set`. Examples of **incorrect** code for this rule: From 62b2278aec0011c93eae17bed8b278114d3379a2 Mon Sep 17 00:00:00 2001 From: G r e y Date: Sun, 12 Apr 2020 00:07:49 -0500 Subject: [PATCH 02/18] fix(eslint-plugin): [no-empty-interface] use suggestion fixer for ambient contexts (#1880) --- .../src/rules/no-empty-interface.ts | 54 ++++++++++++++----- .../tests/rules/no-empty-interface.test.ts | 36 +++++++++++++ 2 files changed, 78 insertions(+), 12 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-empty-interface.ts b/packages/eslint-plugin/src/rules/no-empty-interface.ts index 2e9e61c1e0e..d279e9bd4f6 100644 --- a/packages/eslint-plugin/src/rules/no-empty-interface.ts +++ b/packages/eslint-plugin/src/rules/no-empty-interface.ts @@ -1,4 +1,8 @@ import * as util from '../util'; +import { + AST_NODE_TYPES, + TSESLint, +} from '@typescript-eslint/experimental-utils'; type Options = [ { @@ -43,6 +47,7 @@ export default util.createRule({ return { TSInterfaceDeclaration(node): void { const sourceCode = context.getSourceCode(); + const filename = context.getFilename(); if (node.body.body.length !== 0) { // interface contains members --> Nothing to report @@ -58,21 +63,46 @@ export default util.createRule({ } else if (extend.length === 1) { // interface extends exactly 1 interface --> Report depending on rule setting if (!allowSingleExtends) { + const fix = (fixer: TSESLint.RuleFixer): TSESLint.RuleFix => { + let typeParam = ''; + if (node.typeParameters) { + typeParam = sourceCode.getText(node.typeParameters); + } + return fixer.replaceText( + node, + `type ${sourceCode.getText( + node.id, + )}${typeParam} = ${sourceCode.getText(extend[0])}`, + ); + }; + + // Check if interface is within ambient declaration + let useAutoFix = true; + if (util.isDefinitionFile(filename)) { + const scope = context.getScope(); + if ( + scope.block.parent && + scope.block.parent.type === + AST_NODE_TYPES.TSModuleDeclaration && + scope.block.parent.declare + ) { + useAutoFix = false; + } + } + context.report({ node: node.id, messageId: 'noEmptyWithSuper', - fix: fixer => { - let typeParam = ''; - if (node.typeParameters) { - typeParam = sourceCode.getText(node.typeParameters); - } - return fixer.replaceText( - node, - `type ${sourceCode.getText( - node.id, - )}${typeParam} = ${sourceCode.getText(extend[0])}`, - ); - }, + ...(useAutoFix + ? { fix } + : { + suggest: [ + { + messageId: 'noEmptyWithSuper', + fix, + }, + ], + }), }); } } diff --git a/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts b/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts index 840e729f183..d0281ec77a0 100644 --- a/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts +++ b/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts @@ -148,5 +148,41 @@ type Foo = Bar }, ], }, + { + filename: 'test.d.ts', + code: ` +declare module FooBar { + type Baz = typeof baz; + export interface Bar extends Baz {} +} + `.trimRight(), + errors: [ + { + messageId: 'noEmptyWithSuper', + line: 4, + column: 20, + endLine: 4, + endColumn: 23, + suggestions: [ + { + messageId: 'noEmptyWithSuper', + output: noFormat` +declare module FooBar { + type Baz = typeof baz; + export type Bar = Baz +} + `.trimRight(), + }, + ], + }, + ], + // output matches input because a suggestion was made + output: ` +declare module FooBar { + type Baz = typeof baz; + export interface Bar extends Baz {} +} + `.trimRight(), + }, ], }); From af2c00de62f7e31eaeb88996ebf3f330cc8473b9 Mon Sep 17 00:00:00 2001 From: garyking Date: Sun, 12 Apr 2020 01:38:59 -0400 Subject: [PATCH 03/18] feat(eslint-plugin): [consistent-type-assertions] always allow `const` assertions (#1713) --- .../docs/rules/consistent-type-assertions.md | 2 ++ .../src/rules/consistent-type-assertions.ts | 20 +++++++++++++++++-- .../rules/consistent-type-assertions.test.ts | 18 +++++++++++++++-- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/consistent-type-assertions.md b/packages/eslint-plugin/docs/rules/consistent-type-assertions.md index cdf0fb7656c..43e67b755b5 100644 --- a/packages/eslint-plugin/docs/rules/consistent-type-assertions.md +++ b/packages/eslint-plugin/docs/rules/consistent-type-assertions.md @@ -8,6 +8,8 @@ Type assertions are also commonly referred as "type casting" in TypeScript (even In addition to ensuring that type assertions are written in a consistent way, this rule also helps make your codebase more type-safe. +`const` assertions, [introduced in TypeScript 3.4](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions), is always allowed by this rule. Examples of it include `let x = "hello" as const;` and `let x = "hello";`. + ## Options ```ts diff --git a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts index ae54149d0a7..0620822f83c 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts @@ -75,10 +75,27 @@ export default util.createRule({ create(context, [options]) { const sourceCode = context.getSourceCode(); + function isConst(node: TSESTree.TypeNode): boolean { + if (node.type !== AST_NODE_TYPES.TSTypeReference) { + return false; + } + + return ( + node.typeName.type === AST_NODE_TYPES.Identifier && + node.typeName.name === 'const' + ); + } + function reportIncorrectAssertionType( node: TSESTree.TSTypeAssertion | TSESTree.TSAsExpression, ): void { + // If this node is `as const`, then don't report an error. + if (isConst(node.typeAnnotation)) { + return; + } + const messageId = options.assertionStyle; + context.report({ node, messageId, @@ -97,8 +114,7 @@ export default util.createRule({ case AST_NODE_TYPES.TSTypeReference: return ( // Ignore `as const` and `` - (node.typeName.type === AST_NODE_TYPES.Identifier && - node.typeName.name !== 'const') || + !isConst(node) || // Allow qualified names which have dots between identifiers, `Foo.Bar` node.typeName.type === AST_NODE_TYPES.TSQualifiedName ); diff --git a/packages/eslint-plugin/tests/rules/consistent-type-assertions.test.ts b/packages/eslint-plugin/tests/rules/consistent-type-assertions.test.ts index f885d503091..010bb894ad7 100644 --- a/packages/eslint-plugin/tests/rules/consistent-type-assertions.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-type-assertions.test.ts @@ -9,14 +9,12 @@ const ANGLE_BRACKET_TESTS = ` const x = new Generic(); const x = b; const x = [1]; -const x = [1]; const x = ('string'); `; const AS_TESTS = ` const x = new Generic() as Foo; const x = b as A; const x = [1] as readonly number[]; -const x = [1] as const; const x = ('string') as a | b; `; const OBJECT_LITERAL_AS_CASTS = ` @@ -98,6 +96,22 @@ ruleTester.run('consistent-type-assertions', rule, { }, ], }), + { + code: 'const x = [1];', + options: [ + { + assertionStyle: 'never', + }, + ], + }, + { + code: 'const x = [1] as const;', + options: [ + { + assertionStyle: 'never', + }, + ], + }, ], invalid: [ ...batchedSingleLineTests({ From d44c0f9bed2404ca00b020b35fd825929e213398 Mon Sep 17 00:00:00 2001 From: itowlson Date: Sun, 12 Apr 2020 22:09:47 +1200 Subject: [PATCH 04/18] feat(eslint-plugin): [restrict-template-expressions] add option `allowAny` (#1762) --- .../rules/restrict-template-expressions.md | 10 ++++ .../rules/restrict-template-expressions.ts | 10 +++- .../restrict-template-expressions.test.ts | 46 +++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/docs/rules/restrict-template-expressions.md b/packages/eslint-plugin/docs/rules/restrict-template-expressions.md index 549da98eebd..4c52e74445f 100644 --- a/packages/eslint-plugin/docs/rules/restrict-template-expressions.md +++ b/packages/eslint-plugin/docs/rules/restrict-template-expressions.md @@ -59,6 +59,16 @@ const msg1 = `arg = ${arg}`; const msg2 = `arg = ${arg || 'not truthy'}`; ``` +### `allowAny` + +Examples of additional **correct** code for this rule with `{ allowAny: true }`: + +```ts +const user = JSON.parse('{ "name": "foo" }'); +const msg1 = `arg = ${user.name}`; +const msg2 = `arg = ${user.name || 'the user with no name'}`; +``` + ### `allowNullable` Examples of additional **correct** code for this rule with `{ allowNullable: true }`: diff --git a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts index 6ff1627daf5..63b257ffe7f 100644 --- a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts +++ b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts @@ -10,6 +10,7 @@ type Options = [ allowNullable?: boolean; allowNumber?: boolean; allowBoolean?: boolean; + allowAny?: boolean; }, ]; @@ -32,6 +33,7 @@ export default util.createRule({ { type: 'object', properties: { + allowAny: { type: 'boolean' }, allowBoolean: { type: 'boolean' }, allowNullable: { type: 'boolean' }, allowNumber: { type: 'boolean' }, @@ -51,6 +53,7 @@ export default util.createRule({ | 'boolean' | 'null' | 'undefined' + | 'any' | 'other'; const allowedTypes: BaseType[] = [ @@ -58,6 +61,7 @@ export default util.createRule({ ...(options.allowNumber ? (['number', 'bigint'] as const) : []), ...(options.allowBoolean ? (['boolean'] as const) : []), ...(options.allowNullable ? (['null', 'undefined'] as const) : []), + ...(options.allowAny ? (['any'] as const) : []), ]; function isAllowedType(types: BaseType[]): boolean { @@ -127,6 +131,9 @@ export default util.createRule({ if (type.flags & ts.TypeFlags.Undefined) { return ['undefined']; } + if (type.flags & ts.TypeFlags.Any) { + return ['any']; + } if (type.isUnion()) { return type.types @@ -139,7 +146,8 @@ export default util.createRule({ stringType === 'string' || stringType === 'number' || stringType === 'bigint' || - stringType === 'boolean' + stringType === 'boolean' || + stringType === 'any' ) { return [stringType]; } diff --git a/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts b/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts index 3e18e7ac1b1..6a2861efa32 100644 --- a/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts +++ b/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts @@ -115,6 +115,43 @@ ruleTester.run('restrict-template-expressions', rule, { } `, }, + // allowAny + { + options: [{ allowAny: true }], + code: ` + const arg: any = 123; + const msg = \`arg = \${arg}\`; + `, + }, + { + options: [{ allowAny: true }], + code: ` + const arg: any = undefined; + const msg = \`arg = \${arg || 'some-default'}\`; + `, + }, + { + options: [{ allowAny: true }], + code: ` + const user = JSON.parse('{ "name": "foo" }'); + const msg = \`arg = \${user.name}\`; + `, + }, + { + options: [{ allowAny: true }], + code: ` + const user = JSON.parse('{ "name": "foo" }'); + const msg = \`arg = \${user.name || 'the user with no name'}\`; + `, + }, + { + options: [{ allowAny: true }], + code: ` + function test(arg: T) { + return \`arg = \${arg}\`; + } + `, + }, // allowNullable { options: [{ allowNullable: true }], @@ -208,5 +245,14 @@ ruleTester.run('restrict-template-expressions', rule, { `, errors: [{ messageId: 'invalidType', line: 3, column: 27 }], }, + { + options: [{ allowNumber: true, allowBoolean: true, allowNullable: true }], + code: ` + function test(arg: any) { + return \`arg = \${arg}\`; + } + `, + errors: [{ messageId: 'invalidType', line: 3, column: 27 }], + }, ], }); From 6b4680b6e7343d9d98fa1de170f387a36d98b73e Mon Sep 17 00:00:00 2001 From: Zen <843968788@qq.com> Date: Mon, 13 Apr 2020 03:50:13 +0800 Subject: [PATCH 05/18] fix(eslint-plugin): [unbound-method] ignore assignments _to_ methods (#1736) --- .../eslint-plugin/src/rules/unbound-method.ts | 3 ++ .../tests/rules/unbound-method.test.ts | 31 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/packages/eslint-plugin/src/rules/unbound-method.ts b/packages/eslint-plugin/src/rules/unbound-method.ts index 1e9cb9131ad..629aec50cca 100644 --- a/packages/eslint-plugin/src/rules/unbound-method.ts +++ b/packages/eslint-plugin/src/rules/unbound-method.ts @@ -241,6 +241,9 @@ function isSafeUse(node: TSESTree.Node): boolean { case AST_NODE_TYPES.BinaryExpression: return ['instanceof', '==', '!=', '===', '!=='].includes(parent.operator); + case AST_NODE_TYPES.AssignmentExpression: + return parent.operator === '=' && node === parent.left; + case AST_NODE_TYPES.TSNonNullExpression: case AST_NODE_TYPES.TSAsExpression: case AST_NODE_TYPES.TSTypeAssertion: diff --git a/packages/eslint-plugin/tests/rules/unbound-method.test.ts b/packages/eslint-plugin/tests/rules/unbound-method.test.ts index 10f0b549762..733b81e16b1 100644 --- a/packages/eslint-plugin/tests/rules/unbound-method.test.ts +++ b/packages/eslint-plugin/tests/rules/unbound-method.test.ts @@ -147,6 +147,9 @@ ruleTester.run('unbound-method', rule, { "typeof ContainsMethods.boundStatic === 'function';", "typeof ContainsMethods.unboundStatic === 'function';", + + 'instance.unbound = () => {};', + 'instance.unbound = instance.unbound.bind(instance);', ].map(addContainsMethodsClass), ` interface RecordA { @@ -211,6 +214,15 @@ const x: OptionalMethod = {}; declare const myCondition: boolean; if (myCondition || x.mightBeDefined) { console.log('hello world'); +} + `, + // https://github.com/typescript-eslint/typescript-eslint/issues/1256 + ` +class A { + unbound(): void { + this.unbound = undefined; + this.unbound = this.unbound.bind(this); + } } `, ], @@ -336,5 +348,24 @@ const x = CommunicationError.prototype.foo; }, ], }, + { + code: ` +class Foo { + unbound() {} +} +const instance = new Foo(); + +let x; + +x = instance.unbound; // THIS SHOULD ERROR +instance.unbound = x; // THIS SHOULD NOT + `, + errors: [ + { + line: 9, + messageId: 'unbound', + }, + ], + }, ], }); From f5edb9938c33f8b68f026eba00db3abe9359ced3 Mon Sep 17 00:00:00 2001 From: Susisu Date: Mon, 13 Apr 2020 05:29:14 +0900 Subject: [PATCH 06/18] feat(eslint-plugin): [no-base-to-string] add option to ignore tagged templates (#1763) --- .../docs/rules/no-base-to-string.md | 26 ++++++++++++++ .../src/rules/no-base-to-string.ts | 34 ++++++++++++++++--- .../tests/rules/no-base-to-string.test.ts | 26 ++++++++++++++ 3 files changed, 81 insertions(+), 5 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/no-base-to-string.md b/packages/eslint-plugin/docs/rules/no-base-to-string.md index 4e73eb6339a..68bd82568b1 100644 --- a/packages/eslint-plugin/docs/rules/no-base-to-string.md +++ b/packages/eslint-plugin/docs/rules/no-base-to-string.md @@ -52,6 +52,32 @@ const literalWithToString = { `Value: ${literalWithToString}`; ``` +## Options + +The rule accepts an options object with the following properties: + +```ts +type Options = { + // if true, interpolated expressions in tagged templates will not be checked + ignoreTaggedTemplateExpressions?: boolean; +}; + +const defaults = { + ignoreTaggedTemplateExpressions: false, +}; +``` + +### `ignoreTaggedTemplateExpressions` + +This allows to skip checking tagged templates, for cases where the tags do not necessarily stringify interpolated values. + +Examples of additional **correct** code for this rule with `{ ignoreTaggedTemplateExpressions: true }`: + +```ts +function tag() {} +tag`${{}}`; +``` + ## When Not To Use It If you don't mind `"[object Object]"` in your strings, then you will not need this rule. diff --git a/packages/eslint-plugin/src/rules/no-base-to-string.ts b/packages/eslint-plugin/src/rules/no-base-to-string.ts index 98a37c74084..47803f8e2aa 100644 --- a/packages/eslint-plugin/src/rules/no-base-to-string.ts +++ b/packages/eslint-plugin/src/rules/no-base-to-string.ts @@ -12,7 +12,14 @@ enum Usefulness { Sometimes = 'may', } -export default util.createRule({ +type Options = [ + { + ignoreTaggedTemplateExpressions?: boolean; + }, +]; +type MessageIds = 'baseToString'; + +export default util.createRule({ name: 'no-base-to-string', meta: { docs: { @@ -26,11 +33,22 @@ export default util.createRule({ baseToString: "'{{name}} {{certainty}} evaluate to '[object Object]' when stringified.", }, - schema: [], + schema: [ + { + type: 'object', + properties: { + ignoreTaggedTemplateExpressions: { + type: 'boolean', + default: false, + }, + }, + additionalProperties: false, + }, + ], type: 'suggestion', }, - defaultOptions: [], - create(context) { + defaultOptions: [{ ignoreTaggedTemplateExpressions: false }], + create(context, [options]) { const parserServices = util.getParserServices(context); const typeChecker = parserServices.program.getTypeChecker(); @@ -110,8 +128,14 @@ export default util.createRule({ const memberExpr = node.parent as TSESTree.MemberExpression; checkExpression(memberExpr.object); }, - TemplateLiteral(node: TSESTree.TemplateLiteral): void { + if ( + options.ignoreTaggedTemplateExpressions && + node.parent && + node.parent.type === AST_NODE_TYPES.TaggedTemplateExpression + ) { + return; + } for (const expression of node.expressions) { checkExpression(expression); } diff --git a/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts b/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts index cd2e4b166a9..59abfbb1f60 100644 --- a/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts +++ b/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts @@ -70,6 +70,17 @@ const literalWithToString = { 'let _ = {} ^ {};', 'let _ = {} << {};', 'let _ = {} >> {};', + { + code: ` + function tag() {} + tag\`\${{}}\`; + `, + options: [ + { + ignoreTaggedTemplateExpressions: true, + }, + ], + }, ], invalid: [ { @@ -84,6 +95,21 @@ const literalWithToString = { }, ], }, + { + code: ` + function tag() {} + tag\`\${{}}\`; + `, + errors: [ + { + data: { + certainty: 'will', + name: '{}', + }, + messageId: 'baseToString', + }, + ], + }, { code: '({}.toString());', errors: [ From 87b7dbbc8d25d059e34888ec28026bfb4ade2215 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Sun, 12 Apr 2020 15:38:40 -0700 Subject: [PATCH 07/18] docs(eslint-plugin): cleanup: standardise extension doc style, mark deprecated rules (#1887) --- .../eslint-plugin/docs/rules/ban-ts-ignore.md | 5 + .../eslint-plugin/docs/rules/brace-style.md | 4 +- .../eslint-plugin/docs/rules/camelcase.md | 206 +---- .../docs/rules/class-name-casing.md | 5 + .../eslint-plugin/docs/rules/comma-spacing.md | 2 +- .../docs/rules/default-param-last.md | 17 +- .../docs/rules/func-call-spacing.md | 8 +- .../docs/rules/generic-type-naming.md | 9 +- packages/eslint-plugin/docs/rules/indent.md | 709 +----------------- .../docs/rules/interface-name-prefix.md | 5 + .../eslint-plugin/docs/rules/member-naming.md | 5 + .../docs/rules/member-ordering.md | 170 ++--- .../docs/rules/no-array-constructor.md | 21 +- .../docs/rules/no-dupe-class-members.md | 6 +- .../docs/rules/no-empty-function.md | 72 +- .../docs/rules/no-extra-parens.md | 14 +- .../eslint-plugin/docs/rules/no-extra-semi.md | 7 +- .../docs/rules/no-magic-numbers.md | 93 ++- .../eslint-plugin/docs/rules/no-this-alias.md | 14 +- .../docs/rules/no-untyped-public-signature.md | 12 +- .../docs/rules/no-unused-expressions.md | 7 +- .../docs/rules/no-unused-vars.md | 301 +------- .../docs/rules/no-use-before-define.md | 174 +---- .../docs/rules/no-useless-constructor.md | 86 +-- .../docs/rules/prefer-readonly.md | 4 +- packages/eslint-plugin/docs/rules/quotes.md | 4 +- .../eslint-plugin/docs/rules/require-await.md | 47 +- .../eslint-plugin/docs/rules/return-await.md | 125 +-- packages/eslint-plugin/docs/rules/semi.md | 8 +- .../docs/rules/space-before-function-paren.md | 24 +- .../eslint-plugin/src/rules/ban-ts-ignore.ts | 2 +- 31 files changed, 432 insertions(+), 1734 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/ban-ts-ignore.md b/packages/eslint-plugin/docs/rules/ban-ts-ignore.md index 8f69c864669..936d7936afc 100644 --- a/packages/eslint-plugin/docs/rules/ban-ts-ignore.md +++ b/packages/eslint-plugin/docs/rules/ban-ts-ignore.md @@ -4,6 +4,11 @@ This rule has been deprecated in favor of [`ban-ts-comment`](./ban-ts-comment.md Suppressing TypeScript Compiler Errors can be hard to discover. +## DEPRECATED + +This rule has been deprecated in favour of the [`ban-ts-comment`](./ban-ts-comment.md) rule. +It will be removed in a future version of this plugin. + ## Rule Details Does not allow the use of `// @ts-ignore` comments. diff --git a/packages/eslint-plugin/docs/rules/brace-style.md b/packages/eslint-plugin/docs/rules/brace-style.md index c8268dea44e..06dfa1619fb 100644 --- a/packages/eslint-plugin/docs/rules/brace-style.md +++ b/packages/eslint-plugin/docs/rules/brace-style.md @@ -3,11 +3,11 @@ ## Rule Details This rule extends the base [`eslint/brace-style`](https://eslint.org/docs/rules/brace-style) rule. -It supports all options and features of the base rule. +It adds support for `enum`, `interface`, `namespace` and `module` declarations. ## How to use -```cjson +```jsonc { // note you must disable the base rule as it can report incorrect errors "brace-style": "off", diff --git a/packages/eslint-plugin/docs/rules/camelcase.md b/packages/eslint-plugin/docs/rules/camelcase.md index f0b49d0420f..89cd63aa929 100644 --- a/packages/eslint-plugin/docs/rules/camelcase.md +++ b/packages/eslint-plugin/docs/rules/camelcase.md @@ -1,135 +1,43 @@ # Enforce camelCase naming convention (`camelcase`) -When it comes to naming variables, style guides generally fall into one of two -camps: camelCase (`variableName`) and underscores (`variable_name`). This rule -focuses on using the camelCase approach. If your style guide calls for -camelCasing your variable names, then this rule is for you! +## DEPRECATED -## Rule Details +This rule has been deprecated in favour of the [`naming-convention`](./naming-convention.md) rule. +It will be removed in a future version of this plugin. -This rule looks for any underscores (`_`) located within the source code. -It ignores leading and trailing underscores and only checks those in the middle -of a variable name. If ESLint decides that the variable is a constant -(all uppercase), then no warning will be thrown. Otherwise, a warning will be -thrown. This rule only flags definitions and assignments but not function calls. -In case of ES6 `import` statements, this rule only targets the name of the -variable that will be imported into the local module scope. +## Rule Details -**_This rule was taken from the ESLint core rule `camelcase`._** -**_Available options and test cases may vary depending on the version of ESLint installed in the system._** +This rule extends the base [`eslint/camelcase`](https://eslint.org/docs/rules/camelcase) rule. +It adds support for numerous TypeScript features. -## Options +## How to use -```cjson +```jsonc { - // note you must disable the base rule as it can report incorrect errors - "camelcase": "off", - "@typescript-eslint/camelcase": ["error", { "properties": "always" }] + // note you must disable the base rule as it can report incorrect errors + "camelcase": "off", + "@typescript-eslint/camelcase": ["error"] } ``` -This rule has an object option: - -- `"properties": "never"` (default) does not check property names -- `"properties": "always"` enforces camelCase style for property names -- `"genericType": "never"` (default) does not check generic identifiers -- `"genericType": "always"` enforces camelCase style for generic identifiers -- `"ignoreDestructuring": false` (default) enforces camelCase style for destructured identifiers -- `"ignoreDestructuring": true` does not check destructured identifiers -- `allow` (`string[]`) list of properties to accept. Accept regex. - -### properties: "always" - -Examples of **incorrect** code for this rule with the default `{ "properties": "always" }` option: - -```js -/*eslint @typescript-eslint/camelcase: "error"*/ - -import { no_camelcased } from 'external-module'; - -var my_favorite_color = '#112C85'; - -function do_something() { - // ... -} - -obj.do_something = function() { - // ... -}; +## Options -function foo({ no_camelcased }) { - // ... -} +See [`eslint/camelcase` options](https://eslint.org/docs/rules/camelcase#options). +This rule adds the following options: -function foo({ isCamelcased: no_camelcased }) { - // ... +```ts +interface Options extends BaseCamelcaseOptions { + genericType?: 'always' | 'never'; } -function foo({ no_camelcased = 'default value' }) { - // ... -} - -var obj = { - my_pref: 1, +const defaultOptions: Options = { + ...baseCamelcaseDefaultOptions, + genericType: 'never', }; - -var { category_id = 1 } = query; - -var { foo: no_camelcased } = bar; - -var { foo: bar_baz = 1 } = quz; -``` - -Examples of **correct** code for this rule with the default `{ "properties": "always" }` option: - -```js -/*eslint @typescript-eslint/camelcase: "error"*/ - -import { no_camelcased as camelCased } from 'external-module'; - -var myFavoriteColor = '#112C85'; -var _myFavoriteColor = '#112C85'; -var myFavoriteColor_ = '#112C85'; -var MY_FAVORITE_COLOR = '#112C85'; -var foo = bar.baz_boom; -var foo = { qux: bar.baz_boom }; - -obj.do_something(); -do_something(); -new do_something(); - -var { category_id: category } = query; - -function foo({ isCamelCased }) { - // ... -} - -function foo({ isCamelCased: isAlsoCamelCased }) { - // ... -} - -function foo({ isCamelCased = 'default value' }) { - // ... -} - -var { categoryId = 1 } = query; - -var { foo: isCamelCased } = bar; - -var { foo: isCamelCased = 1 } = quz; ``` -### `properties: "never"` - -Examples of **correct** code for this rule with the `{ "properties": "never" }` option: - -```js -/*eslint @typescript-eslint/camelcase: ["error", {properties: "never"}]*/ - -var obj = { - my_pref: 1, -}; -``` +- `"genericType": "never"` (default) does not check generic identifiers +- `"genericType": "always"` enforces camelCase style for generic identifiers ### `genericType: "always"` @@ -225,74 +133,4 @@ class Foo { } ``` -### `ignoreDestructuring: false` - -Examples of **incorrect** code for this rule with the default `{ "ignoreDestructuring": false }` option: - -```js -/*eslint @typescript-eslint/camelcase: "error"*/ - -var { category_id } = query; - -var { category_id = 1 } = query; - -var { category_id: category_id } = query; - -var { category_id: category_alias } = query; - -var { category_id: categoryId, ...other_props } = query; -``` - -### `ignoreDestructuring: true` - -Examples of **incorrect** code for this rule with the `{ "ignoreDestructuring": true }` option: - -```js -/*eslint @typescript-eslint/camelcase: ["error", {ignoreDestructuring: true}]*/ - -var { category_id: category_alias } = query; - -var { category_id, ...other_props } = query; -``` - -Examples of **correct** code for this rule with the `{ "ignoreDestructuring": true }` option: - -```js -/*eslint @typescript-eslint/camelcase: ["error", {ignoreDestructuring: true}]*/ - -var { category_id } = query; - -var { category_id = 1 } = query; - -var { category_id: category_id } = query; -``` - -## allow - -Examples of **correct** code for this rule with the `allow` option: - -```js -/*eslint @typescript-eslint/camelcase: ["error", {allow: ["UNSAFE_componentWillMount"]}]*/ - -function UNSAFE_componentWillMount() { - // ... -} -``` - -```js -/*eslint @typescript-eslint/camelcase: ["error", {allow: ["^UNSAFE_"]}]*/ - -function UNSAFE_componentWillMount() { - // ... -} - -function UNSAFE_componentWillMount() { - // ... -} -``` - -## When Not To Use It - -If you have established coding standards using a different naming convention (separating words with underscores), turn this rule off. - Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/camelcase.md) diff --git a/packages/eslint-plugin/docs/rules/class-name-casing.md b/packages/eslint-plugin/docs/rules/class-name-casing.md index 38c30609747..ae5d5da7a2e 100644 --- a/packages/eslint-plugin/docs/rules/class-name-casing.md +++ b/packages/eslint-plugin/docs/rules/class-name-casing.md @@ -2,6 +2,11 @@ This rule enforces PascalCase names for classes and interfaces. +## DEPRECATED + +This rule has been deprecated in favour of the [`naming-convention`](./naming-convention.md) rule. +It will be removed in a future version of this plugin. + ## Rule Details This rule aims to make it easy to differentiate classes from regular variables at a glance. diff --git a/packages/eslint-plugin/docs/rules/comma-spacing.md b/packages/eslint-plugin/docs/rules/comma-spacing.md index 145e3607726..0920180416a 100644 --- a/packages/eslint-plugin/docs/rules/comma-spacing.md +++ b/packages/eslint-plugin/docs/rules/comma-spacing.md @@ -7,7 +7,7 @@ It adds support for trailing comma in a types parameters list. ## How to use -```cjson +```jsonc { // note you must disable the base rule as it can report incorrect errors "comma-spacing": "off", diff --git a/packages/eslint-plugin/docs/rules/default-param-last.md b/packages/eslint-plugin/docs/rules/default-param-last.md index f3eb9f27b9a..c9c51df40a3 100644 --- a/packages/eslint-plugin/docs/rules/default-param-last.md +++ b/packages/eslint-plugin/docs/rules/default-param-last.md @@ -2,7 +2,8 @@ ## Rule Details -This rule enforces default or optional parameters to be the last of parameters. +This rule extends the base [`eslint/default-param-last`](https://eslint.org/docs/rules/default-param-last) rule. +It adds support for optional parameters. Examples of **incorrect** code for this rule: @@ -38,4 +39,18 @@ class Foo { } ``` +## How to use + +```jsonc +{ + // note you must disable the base rule as it can report incorrect errors + "default-param-last": "off", + "@typescript-eslint/default-param-last": ["error"] +} +``` + +## Options + +See [`eslint/default-param-last` options](https://eslint.org/docs/rules/default-param-last#options). + Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/default-param-last.md) diff --git a/packages/eslint-plugin/docs/rules/func-call-spacing.md b/packages/eslint-plugin/docs/rules/func-call-spacing.md index aa1d3fde96a..8aa9e7e0cbf 100644 --- a/packages/eslint-plugin/docs/rules/func-call-spacing.md +++ b/packages/eslint-plugin/docs/rules/func-call-spacing.md @@ -1,17 +1,13 @@ # Require or disallow spacing between function identifiers and their invocations (`func-call-spacing`) -When calling a function, developers may insert optional whitespace between the function’s name and the parentheses that invoke it. -This rule requires or disallows spaces between the function name and the opening parenthesis that calls it. - ## Rule Details This rule extends the base [`eslint/func-call-spacing`](https://eslint.org/docs/rules/func-call-spacing) rule. -It supports all options and features of the base rule. -This version adds support for generic type parameters on function calls. +It adds support for generic type parameters on function calls. ## How to use -```cjson +```jsonc { // note you must disable the base rule as it can report incorrect errors "func-call-spacing": "off", diff --git a/packages/eslint-plugin/docs/rules/generic-type-naming.md b/packages/eslint-plugin/docs/rules/generic-type-naming.md index 185b3811d9a..dc964273535 100644 --- a/packages/eslint-plugin/docs/rules/generic-type-naming.md +++ b/packages/eslint-plugin/docs/rules/generic-type-naming.md @@ -3,6 +3,11 @@ It can be helpful to enforce a consistent naming style for generic type variables used within a type. For example, prefixing them with `T` and ensuring a somewhat descriptive name, or enforcing Hungarian notation. +## DEPRECATED + +This rule has been deprecated in favour of the [`naming-convention`](./naming-convention.md) rule. +It will be removed in a future version of this plugin. + ## Rule Details This rule allows you to enforce conventions over type variables. By default, it does nothing. @@ -13,7 +18,7 @@ The rule takes a single string option, which is a regular expression that type v Examples of **correct** code with a configuration of `'^T[A-Z][a-zA-Z]+$'`: -```typescript +```ts type ReadOnly = { readonly [TKey in keyof TType]: TType[TKey]; }; @@ -25,7 +30,7 @@ interface SimpleMap { Examples of **incorrect** code with a configuration of `'^T[A-Z][a-zA-Z]+$'`: -```typescript +```ts type ReadOnly = { readonly [Key in keyof T]: T[Key] }; interface SimpleMap { diff --git a/packages/eslint-plugin/docs/rules/indent.md b/packages/eslint-plugin/docs/rules/indent.md index 4636cfeaeb5..023c774657a 100644 --- a/packages/eslint-plugin/docs/rules/indent.md +++ b/packages/eslint-plugin/docs/rules/indent.md @@ -1,713 +1,24 @@ # Enforce consistent indentation (`indent`) -There are several common guidelines which require specific indentation of nested blocks and statements, like: - -```js -function hello(indentSize, type) { - if (indentSize === 4 && type !== 'tab') { - console.log('Each next indentation will increase on 4 spaces'); - } -} -``` - -These are the most common scenarios recommended in different style guides: - -- Two spaces, not longer and no tabs: Google, npm, NodeJS, Idiomatic, Felix -- Tabs: jQuery -- Four spaces: Crockford +## PLEASE READ THIS ISSUE BEFORE USING THIS RULE [#1824](https://github.com/typescript-eslint/typescript-eslint/issues/1824) ## Rule Details -This rule enforces a consistent indentation style. The default style is `4 spaces`. - -## Options - -This rule has a mixed option: +This rule extends the base [`eslint/indent`](https://eslint.org/docs/rules/indent) rule. +It adds support for TypeScript nodes. -For example, for 2-space indentation: +## How to use -```cjson +```jsonc { - // note you must disable the base rule as it can report incorrect errors - "indent": "off", - "@typescript-eslint/indent": ["error", 2] + // note you must disable the base rule as it can report incorrect errors + "indent": "off", + "@typescript-eslint/indent": ["error"] } ``` -Or for tabbed indentation: - -```cjson -{ - // note you must disable the base rule as it can report incorrect errors - "indent": "off", - "@typescript-eslint/indent": ["error", "tab"] -} -``` - -Examples of **incorrect** code for this rule with the default options: - - -```js -/*eslint @typescript-eslint/indent: "error"*/ - -if (a) { - b=c; - function foo(d) { - e=f; - } -} -``` - -Examples of **correct** code for this rule with the default options: - - -```js -/*eslint @typescript-eslint/indent: "error"*/ - -if (a) { - b=c; - function foo(d) { - e=f; - } -} -``` - -This rule has an object option: - -- `"SwitchCase"` (default: 0) enforces indentation level for `case` clauses in `switch` statements -- `"VariableDeclarator"` (default: 1) enforces indentation level for `var` declarators; can also take an object to define separate rules for `var`, `let` and `const` declarations. -- `"outerIIFEBody"` (default: 1) enforces indentation level for file-level IIFEs. -- `"MemberExpression"` (default: 1) enforces indentation level for multi-line property chains. This can also be set to `"off"` to disable checking for `MemberExpression` indentation. -- `"FunctionDeclaration"` takes an object to define rules for function declarations. - - `parameters` (default: 1) enforces indentation level for parameters in a function declaration. This can either be a number indicating indentation level, or the string `"first"` indicating that all parameters of the declaration must be aligned with the first parameter. This can also be set to `"off"` to disable checking for `FunctionDeclaration` parameters. - - `body` (default: 1) enforces indentation level for the body of a function declaration. -- `"FunctionExpression"` takes an object to define rules for function expressions. - - `parameters` (default: 1) enforces indentation level for parameters in a function expression. This can either be a number indicating indentation level, or the string `"first"` indicating that all parameters of the expression must be aligned with the first parameter. This can also be set to `"off"` to disable checking for `FunctionExpression` parameters. - - `body` (default: 1) enforces indentation level for the body of a function expression. -- `"CallExpression"` takes an object to define rules for function call expressions. - - `arguments` (default: 1) enforces indentation level for arguments in a call expression. This can either be a number indicating indentation level, or the string `"first"` indicating that all arguments of the expression must be aligned with the first argument. This can also be set to `"off"` to disable checking for `CallExpression` arguments. -- `"ArrayExpression"` (default: 1) enforces indentation level for elements in arrays. It can also be set to the string `"first"`, indicating that all the elements in the array should be aligned with the first element. This can also be set to `"off"` to disable checking for array elements. -- `"ObjectExpression"` (default: 1) enforces indentation level for properties in objects. It can be set to the string `"first"`, indicating that all properties in the object should be aligned with the first property. This can also be set to `"off"` to disable checking for object properties. -- `"ImportDeclaration"` (default: 1) enforces indentation level for import statements. It can be set to the string `"first"`, indicating that all imported members from a module should be aligned with the first member in the list. This can also be set to `"off"` to disable checking for imported module members. -- `"flatTernaryExpressions": true` (`false` by default) requires no indentation for ternary expressions which are nested in other ternary expressions. -- `"ignoredNodes"` accepts an array of [selectors](https://eslint.org/docs/developer-guide/selectors). If an AST node is matched by any of the selectors, the indentation of tokens which are direct children of that node will be ignored. This can be used as an escape hatch to relax the rule if you disagree with the indentation that it enforces for a particular syntactic pattern. -- `"ignoreComments"` (default: false) can be used when comments do not need to be aligned with nodes on the previous or next line. - -Level of indentation denotes the multiple of the indent specified. Example: - -- Indent of 4 spaces with `VariableDeclarator` set to `2` will indent the multi-line variable declarations with 8 spaces. -- Indent of 2 spaces with `VariableDeclarator` set to `2` will indent the multi-line variable declarations with 4 spaces. -- Indent of 2 spaces with `VariableDeclarator` set to `{"var": 2, "let": 2, "const": 3}` will indent the multi-line variable declarations with 4 spaces for `var` and `let`, 6 spaces for `const` statements. -- Indent of tab with `VariableDeclarator` set to `2` will indent the multi-line variable declarations with 2 tabs. -- Indent of 2 spaces with `SwitchCase` set to `0` will not indent `case` clauses with respect to `switch` statements. -- Indent of 2 spaces with `SwitchCase` set to `1` will indent `case` clauses with 2 spaces with respect to `switch` statements. -- Indent of 2 spaces with `SwitchCase` set to `2` will indent `case` clauses with 4 spaces with respect to `switch` statements. -- Indent of tab with `SwitchCase` set to `2` will indent `case` clauses with 2 tabs with respect to `switch` statements. -- Indent of 2 spaces with `MemberExpression` set to `0` will indent the multi-line property chains with 0 spaces. -- Indent of 2 spaces with `MemberExpression` set to `1` will indent the multi-line property chains with 2 spaces. -- Indent of 2 spaces with `MemberExpression` set to `2` will indent the multi-line property chains with 4 spaces. -- Indent of 4 spaces with `MemberExpression` set to `0` will indent the multi-line property chains with 0 spaces. -- Indent of 4 spaces with `MemberExpression` set to `1` will indent the multi-line property chains with 4 spaces. -- Indent of 4 spaces with `MemberExpression` set to `2` will indent the multi-line property chains with 8 spaces. - -### tab - -Examples of **incorrect** code for this rule with the `"tab"` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", "tab"]*/ - -if (a) { - b=c; -function foo(d) { - e=f; - } -} -``` - -Examples of **correct** code for this rule with the `"tab"` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", "tab"]*/ - -if (a) { -/*tab*/b=c; -/*tab*/function foo(d) { -/*tab*//*tab*/e=f; -/*tab*/} -} -``` - -### `SwitchCase` - -Examples of **incorrect** code for this rule with the `2, { "SwitchCase": 1 }` options: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "SwitchCase": 1 }]*/ - -switch(a){ -case "a": - break; -case "b": - break; -} -``` - -Examples of **correct** code for this rule with the `2, { "SwitchCase": 1 }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "SwitchCase": 1 }]*/ - -switch(a){ - case "a": - break; - case "b": - break; -} -``` - -### `VariableDeclarator` - -Examples of **incorrect** code for this rule with the `2, { "VariableDeclarator": 1 }` options: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "VariableDeclarator": 1 }]*/ -/*eslint-env es6*/ - -var a, - b, - c; -let a, - b, - c; -const a = 1, - b = 2, - c = 3; -``` - -Examples of **correct** code for this rule with the `2, { "VariableDeclarator": 1 }` options: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "VariableDeclarator": 1 }]*/ -/*eslint-env es6*/ - -var a, - b, - c; -let a, - b, - c; -const a = 1, - b = 2, - c = 3; -``` - -Examples of **correct** code for this rule with the `2, { "VariableDeclarator": 2 }` options: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "VariableDeclarator": 2 }]*/ -/*eslint-env es6*/ - -var a, - b, - c; -let a, - b, - c; -const a = 1, - b = 2, - c = 3; -``` - -Examples of **correct** code for this rule with the `2, { "VariableDeclarator": { "var": 2, "let": 2, "const": 3 } }` options: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "VariableDeclarator": { "var": 2, "let": 2, "const": 3 } }]*/ -/*eslint-env es6*/ - -var a, - b, - c; -let a, - b, - c; -const a = 1, - b = 2, - c = 3; -``` - -### `outerIIFEBody` - -Examples of **incorrect** code for this rule with the options `2, { "outerIIFEBody": 0 }`: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "outerIIFEBody": 0 }]*/ - -(function() { - - function foo(x) { - return x + 1; - } - -})(); - - -if(y) { -console.log('foo'); -} -``` - -Examples of **correct** code for this rule with the options `2, {"outerIIFEBody": 0}`: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "outerIIFEBody": 0 }]*/ - -(function() { - -function foo(x) { - return x + 1; -} - -})(); - - -if(y) { - console.log('foo'); -} -``` - -### `MemberExpression` - -Examples of **incorrect** code for this rule with the `2, { "MemberExpression": 1 }` options: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "MemberExpression": 1 }]*/ - -foo -.bar -.baz() -``` - -Examples of **correct** code for this rule with the `2, { "MemberExpression": 1 }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "MemberExpression": 1 }]*/ - -foo - .bar - .baz(); -``` - -### `FunctionDeclaration` - -Examples of **incorrect** code for this rule with the `2, { "FunctionDeclaration": {"body": 1, "parameters": 2} }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "FunctionDeclaration": {"body": 1, "parameters": 2} }]*/ - -function foo(bar, - baz, - qux) { - qux(); -} -``` - -Examples of **correct** code for this rule with the `2, { "FunctionDeclaration": {"body": 1, "parameters": 2} }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "FunctionDeclaration": {"body": 1, "parameters": 2} }]*/ - -function foo(bar, - baz, - qux) { - qux(); -} -``` - -Examples of **incorrect** code for this rule with the `2, { "FunctionDeclaration": {"parameters": "first"} }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, {"FunctionDeclaration": {"parameters": "first"}}]*/ - -function foo(bar, baz, - qux, boop) { - qux(); -} -``` - -Examples of **correct** code for this rule with the `2, { "FunctionDeclaration": {"parameters": "first"} }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, {"FunctionDeclaration": {"parameters": "first"}}]*/ - -function foo(bar, baz, - qux, boop) { - qux(); -} -``` - -### `FunctionExpression` - -Examples of **incorrect** code for this rule with the `2, { "FunctionExpression": {"body": 1, "parameters": 2} }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "FunctionExpression": {"body": 1, "parameters": 2} }]*/ - -var foo = function(bar, - baz, - qux) { - qux(); -} -``` - -Examples of **correct** code for this rule with the `2, { "FunctionExpression": {"body": 1, "parameters": 2} }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "FunctionExpression": {"body": 1, "parameters": 2} }]*/ - -var foo = function(bar, - baz, - qux) { - qux(); -} -``` - -Examples of **incorrect** code for this rule with the `2, { "FunctionExpression": {"parameters": "first"} }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, {"FunctionExpression": {"parameters": "first"}}]*/ - -var foo = function(bar, baz, - qux, boop) { - qux(); -} -``` - -Examples of **correct** code for this rule with the `2, { "FunctionExpression": {"parameters": "first"} }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, {"FunctionExpression": {"parameters": "first"}}]*/ - -var foo = function(bar, baz, - qux, boop) { - qux(); -} -``` - -### `CallExpression` - -Examples of **incorrect** code for this rule with the `2, { "CallExpression": {"arguments": 1} }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "CallExpression": {"arguments": 1} }]*/ - -foo(bar, - baz, - qux -); -``` - -Examples of **correct** code for this rule with the `2, { "CallExpression": {"arguments": 1} }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "CallExpression": {"arguments": 1} }]*/ - -foo(bar, - baz, - qux -); -``` - -Examples of **incorrect** code for this rule with the `2, { "CallExpression": {"arguments": "first"} }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, {"CallExpression": {"arguments": "first"}}]*/ - -foo(bar, baz, - baz, boop, beep); -``` - -Examples of **correct** code for this rule with the `2, { "CallExpression": {"arguments": "first"} }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, {"CallExpression": {"arguments": "first"}}]*/ - -foo(bar, baz, - baz, boop, beep); -``` - -### `ArrayExpression` - -Examples of **incorrect** code for this rule with the `2, { "ArrayExpression": 1 }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "ArrayExpression": 1 }]*/ - -var foo = [ - bar, -baz, - qux -]; -``` - -Examples of **correct** code for this rule with the `2, { "ArrayExpression": 1 }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "ArrayExpression": 1 }]*/ - -var foo = [ - bar, - baz, - qux -]; -``` - -Examples of **incorrect** code for this rule with the `2, { "ArrayExpression": "first" }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, {"ArrayExpression": "first"}]*/ - -var foo = [bar, - baz, - qux -]; -``` - -Examples of **correct** code for this rule with the `2, { "ArrayExpression": "first" }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, {"ArrayExpression": "first"}]*/ - -var foo = [bar, - baz, - qux -]; -``` - -### `ObjectExpression` - -Examples of **incorrect** code for this rule with the `2, { "ObjectExpression": 1 }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "ObjectExpression": 1 }]*/ - -var foo = { - bar: 1, -baz: 2, - qux: 3 -}; -``` - -Examples of **correct** code for this rule with the `2, { "ObjectExpression": 1 }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, { "ObjectExpression": 1 }]*/ - -var foo = { - bar: 1, - baz: 2, - qux: 3 -}; -``` - -Examples of **incorrect** code for this rule with the `2, { "ObjectExpression": "first" }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, {"ObjectExpression": "first"}]*/ - -var foo = { bar: 1, - baz: 2 }; -``` - -Examples of **correct** code for this rule with the `2, { "ObjectExpression": "first" }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 2, {"ObjectExpression": "first"}]*/ - -var foo = { bar: 1, - baz: 2 }; -``` - -### `ImportDeclaration` - -Examples of **correct** code for this rule with the `4, { "ImportDeclaration": 1 }` option (the default): - - -```js -/*eslint @typescript-eslint/indent: ["error", 4, { ImportDeclaration: 1 }]*/ - -import { foo, - bar, - baz, -} from 'qux'; - -import { - foo, - bar, - baz, -} from 'qux'; -``` - -Examples of **incorrect** code for this rule with the `4, { ImportDeclaration: "first" }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 4, { ImportDeclaration: "first" }]*/ - -import { foo, - bar, - baz, -} from 'qux'; -``` - -Examples of **correct** code for this rule with the `4, { ImportDeclaration: "first" }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 4, { ImportDeclaration: "first" }]*/ - -import { foo, - bar, - baz, -} from 'qux'; -``` - -### `flatTernaryExpressions` - -Examples of **incorrect** code for this rule with the default `4, { "flatTernaryExpressions": false }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 4, { "flatTernaryExpressions": false }]*/ - -var a = - foo ? bar : - baz ? qux : - boop; -``` - -Examples of **correct** code for this rule with the default `4, { "flatTernaryExpressions": false }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 4, { "flatTernaryExpressions": false }]*/ - -var a = - foo ? bar : - baz ? qux : - boop; -``` - -Examples of **incorrect** code for this rule with the `4, { "flatTernaryExpressions": true }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 4, { "flatTernaryExpressions": true }]*/ - -var a = - foo ? bar : - baz ? qux : - boop; -``` - -Examples of **correct** code for this rule with the `4, { "flatTernaryExpressions": true }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 4, { "flatTernaryExpressions": true }]*/ - -var a = - foo ? bar : - baz ? qux : - boop; -``` - -### `ignoredNodes` - -The following configuration ignores the indentation of `ConditionalExpression` ("ternary expression") nodes: - -Examples of **correct** code for this rule with the `4, { "ignoredNodes": ["ConditionalExpression"] }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 4, { "ignoredNodes": ["ConditionalExpression"] }]*/ - -var a = foo - ? bar - : baz; - -var a = foo - ? bar -: baz; -``` - -The following configuration ignores indentation in the body of IIFEs. - -Examples of **correct** code for this rule with the `4, { "ignoredNodes": ["CallExpression > FunctionExpression.callee > BlockStatement.body"] }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 4, { "ignoredNodes": ["CallExpression > FunctionExpression.callee > BlockStatement.body"] }]*/ - -(function() { - -foo(); -bar(); - -}) -``` - -### `ignoreComments` - -Examples of additional **correct** code for this rule with the `4, { "ignoreComments": true }` option: - - -```js -/*eslint @typescript-eslint/indent: ["error", 4, { "ignoreComments": true }] */ - -if (foo) { - doSomething(); - -// comment intentionally de-indented - doSomethingElse(); -} -``` - -## Compatibility +## Options -- **JSHint**: `indent` -- **JSCS**: [`validateIndentation`](https://jscs-dev.github.io/rule/validateIndentation) +See [`eslint/indent` options](https://eslint.org/docs/rules/indent#options). Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/indent.md) diff --git a/packages/eslint-plugin/docs/rules/interface-name-prefix.md b/packages/eslint-plugin/docs/rules/interface-name-prefix.md index f6439b490fd..6322c9c79d6 100644 --- a/packages/eslint-plugin/docs/rules/interface-name-prefix.md +++ b/packages/eslint-plugin/docs/rules/interface-name-prefix.md @@ -5,6 +5,11 @@ The unprefixed name is then available for a class that provides a standard imple Alternatively, the contributor guidelines for the TypeScript repo suggest [never prefixing](https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines#names) interfaces with `I`. +## DEPRECATED + +This rule has been deprecated in favour of the [`naming-convention`](./naming-convention.md) rule. +It will be removed in a future version of this plugin. + ## Rule Details This rule enforces whether or not the `I` prefix is required for interface names. diff --git a/packages/eslint-plugin/docs/rules/member-naming.md b/packages/eslint-plugin/docs/rules/member-naming.md index e12f4f239b5..0dd8a5a2f29 100644 --- a/packages/eslint-plugin/docs/rules/member-naming.md +++ b/packages/eslint-plugin/docs/rules/member-naming.md @@ -2,6 +2,11 @@ It can be helpful to enforce naming conventions for `private` (and sometimes `protected`) members of an object. For example, prefixing private properties with a `_` allows them to be easily discerned when being inspected by tools that do not have knowledge of TypeScript (such as most debuggers). +## DEPRECATED + +This rule has been deprecated in favour of the [`naming-convention`](./naming-convention.md) rule. +It will be removed in a future version of this plugin. + ## Rule Details This rule allows you to enforce conventions for class property and method names by their visibility. By default, it enforces nothing. diff --git a/packages/eslint-plugin/docs/rules/member-ordering.md b/packages/eslint-plugin/docs/rules/member-ordering.md index cccf2bbbe0a..9a2a4cdee8f 100644 --- a/packages/eslint-plugin/docs/rules/member-ordering.md +++ b/packages/eslint-plugin/docs/rules/member-ordering.md @@ -53,37 +53,37 @@ Note: There is a deprecated syntax to specify the member types as an array. There are multiple ways to specify the member types. The most explicit and granular form is the following: -```json5 +```jsonc [ // Index signature - 'signature', + "signature", // Fields - 'public-static-field', - 'protected-static-field', - 'private-static-field', - 'public-instance-field', - 'protected-instance-field', - 'private-instance-field', - 'public-abstract-field', - 'protected-abstract-field', - 'private-abstract-field', + "public-static-field", + "protected-static-field", + "private-static-field", + "public-instance-field", + "protected-instance-field", + "private-instance-field", + "public-abstract-field", + "protected-abstract-field", + "private-abstract-field", // Constructors - 'public-constructor', - 'protected-constructor', - 'private-constructor', + "public-constructor", + "protected-constructor", + "private-constructor", // Methods - 'public-static-method', - 'protected-static-method', - 'private-static-method', - 'public-instance-method', - 'protected-instance-method', - 'private-instance-method', - 'public-abstract-method', - 'protected-abstract-method', - 'private-abstract-method', + "public-static-method", + "protected-static-method", + "private-static-method", + "public-instance-method", + "protected-instance-method", + "private-instance-method", + "public-abstract-method", + "protected-abstract-method", + "private-abstract-method" ] ``` @@ -93,23 +93,23 @@ Note: If you only specify some of the possible types, the non-specified ones can It is also possible to group member types by their accessibility (`static`, `instance`, `abstract`), ignoring their scope. -```json5 +```jsonc [ // Index signature // No accessibility for index signature. See above. // Fields - 'public-field', // = ['public-static-field', 'public-instance-field']) - 'protected-field', // = ['protected-static-field', 'protected-instance-field']) - 'private-field', // = ['private-static-field', 'private-instance-field']) + "public-field", // = ["public-static-field", "public-instance-field"]) + "protected-field", // = ["protected-static-field", "protected-instance-field"]) + "private-field", // = ["private-static-field", "private-instance-field"]) // Constructors // Only the accessibility of constructors is configurable. See below. // Methods - 'public-method', // = ['public-static-method', 'public-instance-method']) - 'protected-method', // = ['protected-static-method', 'protected-instance-method']) - 'private-method', // = ['private-static-method', 'private-instance-method']) + "public-method", // = ["public-static-method", "public-instance-method"]) + "protected-method", // = ["protected-static-method", "protected-instance-method"]) + "private-method" // = ["private-static-method", "private-instance-method"]) ] ``` @@ -117,23 +117,23 @@ It is also possible to group member types by their accessibility (`static`, `ins Another option is to group the member types by their scope (`public`, `protected`, `private`), ignoring their accessibility. -```json5 +```jsonc [ // Index signature // No scope for index signature. See above. // Fields - 'static-field', // = ['public-static-field', 'protected-static-field', 'private-static-field']) - 'instance-field', // = ['public-instance-field', 'protected-instance-field', 'private-instance-field']) - 'abstract-field', // = ['public-abstract-field', 'protected-abstract-field', 'private-abstract-field']) + "static-field", // = ["public-static-field", "protected-static-field", "private-static-field"]) + "instance-field", // = ["public-instance-field", "protected-instance-field", "private-instance-field"]) + "abstract-field", // = ["public-abstract-field", "protected-abstract-field", "private-abstract-field"]) // Constructors - 'constructor', // = ['public-constructor', 'protected-constructor', 'private-constructor']) + "constructor", // = ["public-constructor", "protected-constructor", "private-constructor"]) // Methods - 'static-method', // = ['public-static-method', 'protected-static-method', 'private-static-method']) - 'instance-method', // = ['public-instance-method', 'protected-instance-method', 'private-instance-method']) - 'abstract-method', // = ['public-abstract-method', 'protected-abstract-method', 'private-abstract-method']) + "static-method", // = ["public-static-method", "protected-static-method", "private-static-method"]) + "instance-method", // = ["public-instance-method", "protected-instance-method", "private-instance-method"]) + "abstract-method" // = ["public-abstract-method", "protected-abstract-method", "private-abstract-method"]) ] ``` @@ -141,21 +141,21 @@ Another option is to group the member types by their scope (`public`, `protected The third grouping option is to ignore both scope and accessibility. -```json5 +```jsonc [ // Index signature // No grouping for index signature. See above. // Fields - 'field', // = ['public-static-field', 'protected-static-field', 'private-static-field', 'public-instance-field', 'protected-instance-field', 'private-instance-field', - // 'public-abstract-field', 'protected-abstract-field', private-abstract-field']) + "field", // = ["public-static-field", "protected-static-field", "private-static-field", "public-instance-field", "protected-instance-field", "private-instance-field", + // "public-abstract-field", "protected-abstract-field", private-abstract-field"]) // Constructors // Only the accessibility of constructors is configurable. See above. // Methods - 'method', // = ['public-static-method', 'protected-static-method', 'private-static-method', 'public-instance-method', 'protected-instance-method', 'private-instance-method', - // 'public-abstract-method', 'protected-abstract-method', 'private-abstract-method']) + "method" // = ["public-static-method", "protected-static-method", "private-static-method", "public-instance-method", "protected-instance-method", "private-instance-method", + // "public-abstract-method", "protected-abstract-method", "private-abstract-method"]) ] ``` @@ -163,65 +163,65 @@ The third grouping option is to ignore both scope and accessibility. The default configuration looks as follows: -```json5 +```jsonc { - default: [ + "default": [ // Index signature - 'signature', + "signature", // Fields - 'public-static-field', - 'protected-static-field', - 'private-static-field', + "public-static-field", + "protected-static-field", + "private-static-field", - 'public-instance-field', - 'protected-instance-field', - 'private-instance-field', + "public-instance-field", + "protected-instance-field", + "private-instance-field", - 'public-abstract-field', - 'protected-abstract-field', - 'private-abstract-field', + "public-abstract-field", + "protected-abstract-field", + "private-abstract-field", - 'public-field', - 'protected-field', - 'private-field', + "public-field", + "protected-field", + "private-field", - 'static-field', - 'instance-field', - 'abstract-field', + "static-field", + "instance-field", + "abstract-field", - 'field', + "field", // Constructors - 'public-constructor', - 'protected-constructor', - 'private-constructor', + "public-constructor", + "protected-constructor", + "private-constructor", - 'constructor', + "constructor", // Methods - 'public-static-method', - 'protected-static-method', - 'private-static-method', + "public-static-method", + "protected-static-method", + "private-static-method", - 'public-instance-method', - 'protected-instance-method', - 'private-instance-method', + "public-instance-method", + "protected-instance-method", + "private-instance-method", - 'public-abstract-method', - 'protected-abstract-method', - 'private-abstract-method', + "public-abstract-method", + "protected-abstract-method", + "private-abstract-method", - 'public-method', - 'protected-method', - 'private-method', + "public-method", + "protected-method", + "private-method", - 'static-method', - 'instance-method', - 'abstract-method', + "static-method", + "instance-method", + "abstract-method", - 'method', - ], + "method" + ] } ``` @@ -749,7 +749,7 @@ type Foo = { It is possible to sort all members within a group alphabetically. -#### Configuration: `{ default: { memberTypes: , order: 'alphabetically' } }` +#### Configuration: `{ default: { memberTypes: , order: "alphabetically" } }` This will apply the default order (see above) and enforce an alphabetic order within each group. @@ -795,7 +795,7 @@ interface Foo { It is also possible to sort all members and ignore the member groups completely. -#### Configuration: `{ default: { memberTypes: 'never', order: 'alphabetically' } }` +#### Configuration: `{ default: { memberTypes: "never", order: "alphabetically" } }` ##### Incorrect example diff --git a/packages/eslint-plugin/docs/rules/no-array-constructor.md b/packages/eslint-plugin/docs/rules/no-array-constructor.md index 3ab59cbc6ff..fb9a68e7429 100644 --- a/packages/eslint-plugin/docs/rules/no-array-constructor.md +++ b/packages/eslint-plugin/docs/rules/no-array-constructor.md @@ -1,10 +1,9 @@ # Disallow generic `Array` constructors (`no-array-constructor`) -Use of the `Array` constructor to construct a new array is generally discouraged in favor of array literal notation because of the single-argument pitfall and because the `Array` global may be redefined. Two exceptions are when the Array constructor is used to intentionally create sparse arrays of a specified size by giving the constructor a single numeric argument, or when using TypeScript type parameters to specify the type of the items of the array (`new Array()`). - ## Rule Details -This rule disallows `Array` constructors. +This rule extends the base [`eslint/no-array-constructor`](https://eslint.org/docs/rules/no-array-constructor) rule. +It adds support for the generically typed `Array` constructor (`new Array()`). Examples of **incorrect** code for this rule: @@ -27,8 +26,18 @@ Array(500); new Array(someOtherArray.length); ``` -## When Not To Use It +## How to use + +```jsonc +{ + // note you must disable the base rule as it can report incorrect errors + "no-array-constructor": "off", + "@typescript-eslint/no-array-constructor": ["error"] +} +``` + +## Options -This rule enforces a nearly universal stylistic concern. That being said, this rule may be disabled if the constructor style is preferred. +See [`eslint/no-array-constructor` options](https://eslint.org/docs/rules/no-array-constructor#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/7685fed33b15763ee3cf7dbe1facfc5ba85173f3/docs/rules/no-array-constructor.md) +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-array-constructor.md) diff --git a/packages/eslint-plugin/docs/rules/no-dupe-class-members.md b/packages/eslint-plugin/docs/rules/no-dupe-class-members.md index d0b3b57d988..ddfc1b9f1bd 100644 --- a/packages/eslint-plugin/docs/rules/no-dupe-class-members.md +++ b/packages/eslint-plugin/docs/rules/no-dupe-class-members.md @@ -7,7 +7,7 @@ It adds support for TypeScript's method overload definitions. ## How to use -```cjson +```jsonc { // note you must disable the base rule as it can report incorrect errors "no-dupe-class-members": "off", @@ -15,4 +15,8 @@ It adds support for TypeScript's method overload definitions. } ``` +## Options + +See [`eslint/no-dupe-class-members` options](https://eslint.org/docs/rules/no-dupe-class-members#options). + Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-dupe-class-members.md) diff --git a/packages/eslint-plugin/docs/rules/no-empty-function.md b/packages/eslint-plugin/docs/rules/no-empty-function.md index 17628cb93c8..df6a3f82583 100644 --- a/packages/eslint-plugin/docs/rules/no-empty-function.md +++ b/packages/eslint-plugin/docs/rules/no-empty-function.md @@ -1,80 +1,24 @@ # Disallow empty functions (`no-empty-function`) -Empty functions can reduce readability because readers need to guess whether it’s intentional or not. So writing a clear comment for empty functions is a good practice. - ## Rule Details -The `@typescript-eslint/no-empty-function` rule extends the `no-empty-function` rule from ESLint core, and adds support for handling TypeScript specific code that would otherwise trigger the rule. +This rule extends the base [`eslint/no-empty-function`](https://eslint.org/docs/rules/no-empty-function) rule. +It adds support for handling TypeScript specific code that would otherwise trigger the rule. One example of valid TypeScript specific code that would otherwise trigger the `no-empty-function` rule is the use of [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) in constructor functions: -```typescript -class Person { - constructor(private firstName: string, private surname: string) {} -} -``` - -The above code is functionally equivalent to: +## How to use -```typescript -class Person { - private firstName: string; - private surname: string; - - constructor(firstName: string, surname: string) { - this.firstName = firstName; - this.surname = surname; - } -} -``` - -Parameter properties enable both the _declaration_ and _initialization_ of member properties in a single location, avoiding the boilerplate & duplication that is common when initializing member properties from parameter values in a constructor function. - -In these cases, although the constructor has an empty function body, it is technically valid and should not trigger an error. - -See the [ESLint documentation](https://eslint.org/docs/rules/no-empty-function) for more details on the `no-empty-function` rule. - -## Rule Changes - -```cjson +```jsonc { - // note you must disable the base rule as it can report incorrect errors - "no-empty-function": "off", - "@typescript-eslint/no-empty-function": "error" + // note you must disable the base rule as it can report incorrect errors + "no-empty-function": "off", + "@typescript-eslint/no-empty-function": ["error"] } ``` ## Options -This rule has an object option: - -- `allow` (`string[]`) - - `"protected-constructors"` - Protected class constructors. - - `"private-constructors"` - Private class constructors. - - [See the other options allowed](https://github.com/eslint/eslint/blob/master/docs/rules/no-empty-function.md#options) - -#### allow: protected-constructors - -Examples of **correct** code for the `{ "allow": ["protected-constructors"] }` option: - -```ts -/*eslint @typescript-eslint/no-empty-function: ["error", { "allow": ["protected-constructors"] }]*/ - -class Foo { - protected constructor() {} -} -``` - -#### allow: private-constructors - -Examples of **correct** code for the `{ "allow": ["private-constructors"] }` option: - -```ts -/*eslint @typescript-eslint/no-empty-function: ["error", { "allow": ["private-constructors"] }]*/ - -class Foo { - private constructor() {} -} -``` +See [`eslint/no-empty-function` options](https://eslint.org/docs/rules/no-empty-function#options). Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-empty-function.md) diff --git a/packages/eslint-plugin/docs/rules/no-extra-parens.md b/packages/eslint-plugin/docs/rules/no-extra-parens.md index bcb21dec28d..8173b2ef1ad 100644 --- a/packages/eslint-plugin/docs/rules/no-extra-parens.md +++ b/packages/eslint-plugin/docs/rules/no-extra-parens.md @@ -1,22 +1,22 @@ # Disallow unnecessary parentheses (`no-extra-parens`) -This rule restricts the use of parentheses to only where they are necessary. - ## Rule Details This rule extends the base [`eslint/no-extra-parens`](https://eslint.org/docs/rules/no-extra-parens) rule. -It supports all options and features of the base rule plus TS type assertions. +It adds support for TypeScript type assertions. ## How to use -```cjson +```jsonc { - // note you must disable the base rule as it can report incorrect errors - "no-extra-parens": "off", - "@typescript-eslint/no-extra-parens": ["error"] + // note you must disable the base rule as it can report incorrect errors + "no-extra-parens": "off", + "@typescript-eslint/no-extra-parens": ["error"] } ``` ## Options See [`eslint/no-extra-parens` options](https://eslint.org/docs/rules/no-extra-parens#options). + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-extra-parens.md) diff --git a/packages/eslint-plugin/docs/rules/no-extra-semi.md b/packages/eslint-plugin/docs/rules/no-extra-semi.md index bed5e29d657..f6865bd8b54 100644 --- a/packages/eslint-plugin/docs/rules/no-extra-semi.md +++ b/packages/eslint-plugin/docs/rules/no-extra-semi.md @@ -3,10 +3,11 @@ ## Rule Details This rule extends the base [`eslint/no-extra-semi`](https://eslint.org/docs/rules/no-extra-semi) rule. +It adds support for class properties. ## How to use -```cjson +```jsonc { // note you must disable the base rule as it can report incorrect errors "no-extra-semi": "off", @@ -14,4 +15,8 @@ This rule extends the base [`eslint/no-extra-semi`](https://eslint.org/docs/rule } ``` +## Options + +See [`eslint/no-extra-semi` options](https://eslint.org/docs/rules/no-extra-semi#options). + Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-extra-semi.md) diff --git a/packages/eslint-plugin/docs/rules/no-magic-numbers.md b/packages/eslint-plugin/docs/rules/no-magic-numbers.md index 377e9272360..f41204c132b 100644 --- a/packages/eslint-plugin/docs/rules/no-magic-numbers.md +++ b/packages/eslint-plugin/docs/rules/no-magic-numbers.md @@ -1,25 +1,72 @@ # Disallow magic numbers (`no-magic-numbers`) -'Magic numbers' are numbers that occur multiple times in code without an explicit meaning. -They should preferably be replaced by named constants. - ## Rule Details -The `@typescript-eslint/no-magic-numbers` rule extends the `no-magic-numbers` rule from ESLint core, and adds support for handling TypeScript specific code that would otherwise trigger the rule. +This rule extends the base [`eslint/no-magic-numbers`](https://eslint.org/docs/rules/no-magic-numbers) rule. +It adds support for: -See the [ESLint documentation](https://eslint.org/docs/rules/no-magic-numbers) for more details on the `no-magic-numbers` rule. +- numeric literal types (`type T = 1`), +- `enum` members (`enum Foo { bar = 1 }`), +- `readonly` class properties (`class Foo { readonly bar = 1 }`). -## Rule Changes +## How to use -```cjson +```jsonc { - // note you must disable the base rule as it can report incorrect errors - "no-magic-numbers": "off", - "@typescript-eslint/no-magic-numbers": ["error", { "ignoreNumericLiteralTypes": true }] + // note you must disable the base rule as it can report incorrect errors + "no-magic-numbers": "off", + "@typescript-eslint/no-magic-numbers": [ + "error", + { + /* options */ + } + ] +} +``` + +## Options + +See [`eslint/no-magic-numbers` options](https://eslint.org/docs/rules/no-magic-numbers#options). +This rule adds the following options: + +```ts +interface Options extends BaseNoMagicNumbersOptions { + ignoreEnums?: boolean; + ignoreNumericLiteralTypes?: boolean; + ignoreReadonlyClassProperties?: boolean; +} + +const defaultOptions: Options = { + ...baseNoMagicNumbersDefaultOptions, + ignoreEnums: false, + ignoreNumericLiteralTypes: false, + ignoreReadonlyClassProperties: false, +}; +``` + +### `ignoreEnums` + +A boolean to specify if enums used in TypeScript are considered okay. `false` by default. + +Examples of **incorrect** code for the `{ "ignoreEnums": false }` option: + +```ts +/*eslint @typescript-eslint/no-magic-numbers: ["error", { "ignoreEnums": false }]*/ + +enum foo = { + SECOND = 1000, } ``` -In addition to the options supported by the `no-magic-numbers` rule in ESLint core, the rule adds the following options: +Examples of **correct** code for the `{ "ignoreEnums": true }` option: + +```ts +/*eslint @typescript-eslint/no-magic-numbers: ["error", { "ignoreEnums": true }]*/ + +enum foo = { + SECOND = 1000, +} +``` ### `ignoreNumericLiteralTypes` @@ -69,28 +116,4 @@ class Foo { } ``` -### `ignoreEnums` - -A boolean to specify if enums used in TypeScript are considered okay. `false` by default. - -Examples of **incorrect** code for the `{ "ignoreEnums": false }` option: - -```ts -/*eslint @typescript-eslint/no-magic-numbers: ["error", { "ignoreEnums": false }]*/ - -enum foo = { - SECOND = 1000, -} -``` - -Examples of **correct** code for the `{ "ignoreEnums": true }` option: - -```ts -/*eslint @typescript-eslint/no-magic-numbers: ["error", { "ignoreEnums": true }]*/ - -enum foo = { - SECOND = 1000, -} -``` - Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-magic-numbers.md) diff --git a/packages/eslint-plugin/docs/rules/no-this-alias.md b/packages/eslint-plugin/docs/rules/no-this-alias.md index 7f5ee719fa0..b5d2afb542c 100644 --- a/packages/eslint-plugin/docs/rules/no-this-alias.md +++ b/packages/eslint-plugin/docs/rules/no-this-alias.md @@ -39,15 +39,15 @@ Examples of **correct** code for this rule: You can pass an object option: -```json5 +```jsonc { - '@typescript-eslint/no-this-alias': [ - 'error', + "@typescript-eslint/no-this-alias": [ + "error", { - allowDestructuring: true, // Allow `const { props, state } = this`; false by default - allowedNames: ['self'], // Allow `const self = this`; `[]` by default - }, - ], + "allowDestructuring": true, // Allow `const { props, state } = this`; false by default + "allowedNames": ["self"] // Allow `const self = this`; `[]` by default + } + ] } ``` diff --git a/packages/eslint-plugin/docs/rules/no-untyped-public-signature.md b/packages/eslint-plugin/docs/rules/no-untyped-public-signature.md index 87a17895c38..ffcd083b57d 100644 --- a/packages/eslint-plugin/docs/rules/no-untyped-public-signature.md +++ b/packages/eslint-plugin/docs/rules/no-untyped-public-signature.md @@ -2,6 +2,11 @@ public methods are meant to be used by code outside of your class. By typing both the parameters and the return type of public methods they will be more readable and easy to use. +## DEPRECATED + +This rule has been deprecated in favour of the [`explicit-module-boundary-types`](./explicit-module-boundary-types.md) rule. +It will be removed in a future version of this plugin. + ## Rule Details This rule aims to ensure that only typed public methods are declared in the code. @@ -46,9 +51,12 @@ This rule, in its default state, does not require any argument. You may pass method names you would like this rule to ignore, like so: -```cjson +```jsonc { - "@typescript-eslint/no-untyped-public-signature": ["error", { "ignoredMethods": ["ignoredMethodName"] }] + "@typescript-eslint/no-untyped-public-signature": [ + "error", + { "ignoredMethods": ["ignoredMethodName"] } + ] } ``` diff --git a/packages/eslint-plugin/docs/rules/no-unused-expressions.md b/packages/eslint-plugin/docs/rules/no-unused-expressions.md index 63674120fbc..cb61c2ed040 100644 --- a/packages/eslint-plugin/docs/rules/no-unused-expressions.md +++ b/packages/eslint-plugin/docs/rules/no-unused-expressions.md @@ -1,16 +1,13 @@ # Disallow unused expressions (`no-unused-expressions`) -This rule aims to eliminate unused expressions which have no effect on the state of the program. - ## Rule Details This rule extends the base [`eslint/no-unused-expressions`](https://eslint.org/docs/rules/no-unused-expressions) rule. -It supports all options and features of the base rule. -This version adds support for numerous typescript features. +It adds support for optional call expressions `x?.()`, and directive in module declarations. ## How to use -```cjson +```jsonc { // note you must disable the base rule as it can report incorrect errors "no-unused-expressions": "off", diff --git a/packages/eslint-plugin/docs/rules/no-unused-vars.md b/packages/eslint-plugin/docs/rules/no-unused-vars.md index a84b4362d61..44cfae690f8 100644 --- a/packages/eslint-plugin/docs/rules/no-unused-vars.md +++ b/packages/eslint-plugin/docs/rules/no-unused-vars.md @@ -1,305 +1,24 @@ # Disallow unused variables (`no-unused-vars`) -Variables that are declared and not used anywhere in the code are most likely an error due to incomplete refactoring. Such variables take up space in the code and can lead to confusion by readers. +## PLEASE READ THIS ISSUE BEFORE USING THIS RULE [#1856](https://github.com/typescript-eslint/typescript-eslint/issues/1856) ## Rule Details -This rule is aimed at eliminating unused variables, functions, and parameters of functions. +This rule extends the base [`eslint/no-unused-vars`](https://eslint.org/docs/rules/no-unused-vars) rule. +It adds support for TypeScript features, such as types. -A variable is considered to be used if any of the following are true: +## How to use -- It represents a function that is called (`doSomething()`) -- It is read (`var y = x`) -- It is passed into a function as an argument (`doSomething(x)`) -- It is read inside of a function that is passed to another function (`doSomething(function() { foo(); })`) - -A variable is _not_ considered to be used if it is only ever assigned to (`var x = 5`) or declared. - -Examples of **incorrect** code for this rule: - -```js -/*eslint no-unused-vars: "error"*/ -/*global some_unused_var*/ - -// It checks variables you have defined as global -some_unused_var = 42; - -var x; - -// Write-only variables are not considered as used. -var y = 10; -y = 5; - -// A read for a modification of itself is not considered as used. -var z = 0; -z = z + 1; - -// By default, unused arguments cause warnings. -(function(foo) { - return 5; -})(); - -// Unused recursive functions also cause warnings. -function fact(n) { - if (n < 2) return 1; - return n * fact(n - 1); -} - -// When a function definition destructures an array, unused entries from the array also cause warnings. -function getY([x, y]) { - return y; -} -``` - -Examples of **correct** code for this rule: - -```js -/*eslint no-unused-vars: "error"*/ - -var x = 10; -alert(x); - -// foo is considered used here -myFunc( - function foo() { - // ... - }.bind(this), -); - -(function(foo) { - return foo; -})(); - -var myFunc; -myFunc = setTimeout(function() { - // myFunc is considered used - myFunc(); -}, 50); - -// Only the second argument from the destructured array is used. -function getY([, y]) { - return y; -} -``` - -### exported - -In environments outside of CommonJS or ECMAScript modules, you may use `var` to create a global variable that may be used by other scripts. You can use the `/* exported variableName */` comment block to indicate that this variable is being exported and therefore should not be considered unused. - -Note that `/* exported */` has no effect for any of the following: - -- when the environment is `node` or `commonjs` -- when `parserOptions.sourceType` is `module` -- when `ecmaFeatures.globalReturn` is `true` - -The line comment `// exported variableName` will not work as `exported` is not line-specific. - -Examples of **correct** code for `/* exported variableName */` operation: - -```js -/* exported global_var */ - -var global_var = 42; -``` - -## Options - -This rule takes one argument which can be a string or an object. The string settings are the same as those of the `vars` property (explained below). - -By default this rule is enabled with `all` option for variables and `after-used` for arguments. - -```CJSON +```jsonc { - "rules": { - // note you must disable the base rule as it can report incorrect errors - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": ["error", { - "vars": "all", - "args": "after-used", - "ignoreRestSiblings": false - }] - } + // note you must disable the base rule as it can report incorrect errors + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": ["error"] } ``` -### `vars` - -The `vars` option has two settings: - -- `all` checks all variables for usage, including those in the global scope. This is the default setting. -- `local` checks only that locally-declared variables are used but will allow global variables to be unused. - -#### `vars: local` - -Examples of **correct** code for the `{ "vars": "local" }` option: - -```js -/*eslint no-unused-vars: ["error", { "vars": "local" }]*/ -/*global some_unused_var */ - -some_unused_var = 42; -``` - -### `varsIgnorePattern` - -The `varsIgnorePattern` option specifies exceptions not to check for usage: variables whose names match a regexp pattern. For example, variables whose names contain `ignored` or `Ignored`. - -Examples of **correct** code for the `{ "varsIgnorePattern": "[iI]gnored" }` option: - -```js -/*eslint no-unused-vars: ["error", { "varsIgnorePattern": "[iI]gnored" }]*/ - -var firstVarIgnored = 1; -var secondVar = 2; -console.log(secondVar); -``` - -### `args` - -The `args` option has three settings: - -- `after-used` - unused positional arguments that occur before the last used argument will not be checked, but all named arguments and all positional arguments after the last used argument will be checked. -- `all` - all named arguments must be used. -- `none` - do not check arguments. - -#### `args: after-used` - -Examples of **incorrect** code for the default `{ "args": "after-used" }` option: - -```js -/*eslint no-unused-vars: ["error", { "args": "after-used" }]*/ - -// 2 errors, for the parameters after the last used parameter (bar) -// "baz" is defined but never used -// "qux" is defined but never used -(function(foo, bar, baz, qux) { - return bar; -})(); -``` - -Examples of **correct** code for the default `{ "args": "after-used" }` option: - -```js -/*eslint no-unused-vars: ["error", {"args": "after-used"}]*/ - -(function(foo, bar, baz, qux) { - return qux; -})(); -``` - -#### `args: all` - -Examples of **incorrect** code for the `{ "args": "all" }` option: - -```js -/*eslint no-unused-vars: ["error", { "args": "all" }]*/ - -// 2 errors -// "foo" is defined but never used -// "baz" is defined but never used -(function(foo, bar, baz) { - return bar; -})(); -``` - -#### `args: none` - -Examples of **correct** code for the `{ "args": "none" }` option: - -```js -/*eslint no-unused-vars: ["error", { "args": "none" }]*/ - -(function(foo, bar, baz) { - return bar; -})(); -``` - -### `ignoreRestSiblings` - -The `ignoreRestSiblings` option is a boolean (default: `false`). Using a [Rest Property](https://github.com/tc39/proposal-object-rest-spread) it is possible to "omit" properties from an object, but by default the sibling properties are marked as "unused". With this option enabled the rest property's siblings are ignored. - -Examples of **correct** code for the `{ "ignoreRestSiblings": true }` option: - -```js -/*eslint no-unused-vars: ["error", { "ignoreRestSiblings": true }]*/ -// 'type' is ignored because it has a rest property sibling. -var { type, ...coords } = data; -``` - -### `argsIgnorePattern` - -The `argsIgnorePattern` option specifies exceptions not to check for usage: arguments whose names match a regexp pattern. For example, variables whose names begin with an underscore. - -Examples of **correct** code for the `{ "argsIgnorePattern": "^_" }` option: - -```js -/*eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }]*/ - -function foo(x, _y) { - return x + 1; -} -foo(); -``` - -### `caughtErrors` - -The `caughtErrors` option is used for `catch` block arguments validation. - -It has two settings: - -- `none` - do not check error objects. This is the default setting. -- `all` - all named arguments must be used. - -#### `caughtErrors: none` - -Not specifying this rule is equivalent of assigning it to `none`. - -Examples of **correct** code for the `{ "caughtErrors": "none" }` option: - -```js -/*eslint no-unused-vars: ["error", { "caughtErrors": "none" }]*/ - -try { - //... -} catch (err) { - console.error('errors'); -} -``` - -#### `caughtErrors: all` - -Examples of **incorrect** code for the `{ "caughtErrors": "all" }` option: - -```js -/*eslint no-unused-vars: ["error", { "caughtErrors": "all" }]*/ - -// 1 error -// "err" is defined but never used -try { - //... -} catch (err) { - console.error('errors'); -} -``` - -### `caughtErrorsIgnorePattern` - -The `caughtErrorsIgnorePattern` option specifies exceptions not to check for usage: catch arguments whose names match a regexp pattern. For example, variables whose names begin with a string 'ignore'. - -Examples of **correct** code for the `{ "caughtErrorsIgnorePattern": "^ignore" }` option: - -```js -/*eslint no-unused-vars: ["error", { "caughtErrorsIgnorePattern": "^ignore" }]*/ - -try { - //... -} catch (ignoreErr) { - console.error('errors'); -} -``` - -## When Not To Use It +## Options -If you don't want to be notified about unused variables or function arguments, you can safely turn this rule off. +See [`eslint/no-unused-vars` options](https://eslint.org/docs/rules/no-unused-vars#options). Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-unused-vars.md) diff --git a/packages/eslint-plugin/docs/rules/no-use-before-define.md b/packages/eslint-plugin/docs/rules/no-use-before-define.md index e6acf604568..569b8130386 100644 --- a/packages/eslint-plugin/docs/rules/no-use-before-define.md +++ b/packages/eslint-plugin/docs/rules/no-use-before-define.md @@ -1,146 +1,46 @@ # Disallow the use of variables before they are defined (`no-use-before-define`) -In JavaScript, prior to ES6, variable and function declarations are hoisted to the top of a scope, so it's possible to use identifiers before their formal declarations in code. This can be confusing and some believe it is best to always declare variables and functions before using them. - -In ES6, block-level bindings (`let` and `const`) introduce a "temporal dead zone" where a `ReferenceError` will be thrown with any attempt to access the variable before its declaration. +## PLEASE READ THIS ISSUE BEFORE USING THIS RULE [#1856](https://github.com/typescript-eslint/typescript-eslint/issues/1856) ## Rule Details -This rule will warn when it encounters a reference to an identifier that has not yet been declared. - -Examples of **incorrect** code for this rule: - -```ts -/*eslint no-use-before-define: "error"*/ -/*eslint-env es6*/ - -alert(a); -var a = 10; - -f(); -function f() {} - -function g() { - return b; -} -var b = 1; - -// With blockBindings: true -{ - alert(c); - let c = 1; -} - -let myVar: StringOrNumber; -type StringOrNumber = string | number; -``` - -Examples of **correct** code for this rule: +This rule extends the base [`eslint/no-use-before-define`](https://eslint.org/docs/rules/no-use-before-define) rule. +It adds support for `type`, `interface` and `enum` declarations. -```ts -/*eslint no-use-before-define: "error"*/ -/*eslint-env es6*/ +## How to use -var a; -a = 10; -alert(a); - -function f() {} -f(1); - -var b = 1; -function g() { - return b; -} - -// With blockBindings: true +```jsonc { - let C; - c++; + // note you must disable the base rule as it can report incorrect errors + "no-use-before-define": "off", + "@typescript-eslint/no-use-before-define": ["error"] } - -type StringOrNumber = string | number; -let myVar: StringOrNumber; ``` ## Options -```json -{ - "no-use-before-define": ["error", { "functions": true, "classes": true }] -} -``` - -- `functions` (`boolean`) - - The flag which shows whether or not this rule checks function declarations. - If this is `true`, this rule warns every reference to a function before the function declaration. - Otherwise, ignores those references. - Function declarations are hoisted, so it's safe. - Default is `true`. -- `classes` (`boolean`) - - The flag which shows whether or not this rule checks class declarations of upper scopes. - If this is `true`, this rule warns every reference to a class before the class declaration. - Otherwise, ignores those references if the declaration is in upper function scopes. - Class declarations are not hoisted, so it might be danger. - Default is `true`. -- `enums` (`boolean`) - - The flag which shows whether or not this rule checks enum declarations of upper scopes. - If this is `true`, this rule warns every reference to a enum before the enum declaration. - Otherwise, ignores those references. - Default is `true`. -- `variables` (`boolean`) - - This flag determines whether or not the rule checks variable declarations in upper scopes. - If this is `true`, the rule warns every reference to a variable before the variable declaration. - Otherwise, the rule ignores a reference if the declaration is in an upper scope, while still reporting the reference if it's in the same scope as the declaration. - Default is `true`. -- `typedefs` (`boolean`, **added** in `@typescript-eslint/eslint-plugin`) - - The flag which shows whether or not this rule checks type declarations. - If this is `true`, this rule warns every reference to a type before the type declaration. - Otherwise, ignores those references. - Type declarations are hoisted, so it's safe. - Default is `true`. - -This rule accepts `"nofunc"` string as an option. -`"nofunc"` is the same as `{ "functions": false, "classes": true }`. - -### `functions` - -Examples of **correct** code for the `{ "functions": false }` option: - -```js -/*eslint no-use-before-define: ["error", { "functions": false }]*/ - -f(); -function f() {} -``` - -### `classes` +See [`eslint/no-use-before-define` options](https://eslint.org/docs/rules/no-use-before-define#options). +This rule adds the following options: -Examples of **incorrect** code for the `{ "classes": false }` option: - -```js -/*eslint no-use-before-define: ["error", { "classes": false }]*/ -/*eslint-env es6*/ - -new A(); -class A {} -``` - -Examples of **correct** code for the `{ "classes": false }` option: - -```js -/*eslint no-use-before-define: ["error", { "classes": false }]*/ -/*eslint-env es6*/ - -function foo() { - return new A(); +```ts +interface Options extends BaseNoMagicNumbersOptions { + enums?: boolean; + typedefs?: boolean; } -class A {} +const defaultOptions: Options = { + ...baseNoMagicNumbersDefaultOptions, + enums: true, + typedefs: true, +}; ``` ### `enums` +The flag which shows whether or not this rule checks enum declarations of upper scopes. +If this is `true`, this rule warns every reference to a enum before the enum declaration. +Otherwise, ignores those references. + Examples of **incorrect** code for the `{ "enums": true }` option: ```ts @@ -176,31 +76,13 @@ enum Foo { } ``` -### `variables` - -Examples of **incorrect** code for the `{ "variables": false }` option: - -```js -/*eslint no-use-before-define: ["error", { "variables": false }]*/ - -console.log(foo); -var foo = 1; -``` - -Examples of **correct** code for the `{ "variables": false }` option: - -```js -/*eslint no-use-before-define: ["error", { "variables": false }]*/ - -function baz() { - console.log(foo); -} - -var foo = 1; -``` - ### `typedefs` +The flag which shows whether or not this rule checks type declarations. +If this is `true`, this rule warns every reference to a type before the type declaration. +Otherwise, ignores those references. +Type declarations are hoisted, so it's safe. + Examples of **correct** code for the `{ "typedefs": false }` option: ```ts diff --git a/packages/eslint-plugin/docs/rules/no-useless-constructor.md b/packages/eslint-plugin/docs/rules/no-useless-constructor.md index 757ae5d4a12..524ad4b92d9 100644 --- a/packages/eslint-plugin/docs/rules/no-useless-constructor.md +++ b/packages/eslint-plugin/docs/rules/no-useless-constructor.md @@ -1,88 +1,26 @@ # Disallow unnecessary constructors (`no-useless-constructor`) -ES2015 provides a default class constructor if one is not specified. As such, it is unnecessary to provide an empty constructor or one that simply delegates into its parent class, as in the following examples: - -```js -class A { - constructor() {} -} - -class A extends B { - constructor(value) { - super(value); - } -} -``` - ## Rule Details -This rule flags class constructors that can be safely removed without changing how the class works. - -## Examples - -Examples of **incorrect** code for this rule: - -```js -/*eslint @typescript-eslint/no-useless-constructor: "error"*/ - -class A { - constructor() {} -} - -class A extends B { - constructor(...args) { - super(...args); - } -} -``` +This rule extends the base [`eslint/no-useless-constructor`](https://eslint.org/docs/rules/no-useless-constructor) rule. +It adds support for: -Examples of **correct** code for this rule: - -```js -/*eslint @typescript-eslint/no-useless-constructor: "error"*/ - -class A {} - -class A { - constructor() { - doSomething(); - } -} - -class A extends B { - constructor() { - super('foo'); - } -} - -class A extends B { - constructor() { - super(); - doSomething(); - } -} - -class A extends B { - constructor(protected name: string) {} -} - -class A extends B { - protected constructor() {} -} -``` +- constructors marked as `protected` / `private` (i.e. marking a constructor as non-public), +- `public` constructors when there is no superclass, +- constructors with only parameter properties. -## Rule Changes +## How to use -```cjson +```jsonc { - // note you must disable the base rule as it can report incorrect errors - "no-useless-constructor": "off", - "@typescript-eslint/no-useless-constructor": "error", + // note you must disable the base rule as it can report incorrect errors + "no-useless-constructor": "off", + "@typescript-eslint/no-useless-constructor": ["error"] } ``` -## When Not To Use It +## Options -If you don't want to be notified about unnecessary constructors, you can safely disable this rule. +See [`eslint/no-useless-constructor` options](https://eslint.org/docs/rules/no-useless-constructor#options). Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-useless-constructor.md) diff --git a/packages/eslint-plugin/docs/rules/prefer-readonly.md b/packages/eslint-plugin/docs/rules/prefer-readonly.md index 6731a758580..9ce16a8c35f 100644 --- a/packages/eslint-plugin/docs/rules/prefer-readonly.md +++ b/packages/eslint-plugin/docs/rules/prefer-readonly.md @@ -52,9 +52,9 @@ This rule, in its default state, does not require any argument. You may pass `"onlyInlineLambdas": true` as a rule option within an object to restrict checking only to members immediately assigned a lambda value. -```cjson +```jsonc { - "@typescript-eslint/prefer-readonly": ["error", { "onlyInlineLambdas": true }] + "@typescript-eslint/prefer-readonly": ["error", { "onlyInlineLambdas": true }] } ``` diff --git a/packages/eslint-plugin/docs/rules/quotes.md b/packages/eslint-plugin/docs/rules/quotes.md index e9c37df36f7..6b63587f6b4 100644 --- a/packages/eslint-plugin/docs/rules/quotes.md +++ b/packages/eslint-plugin/docs/rules/quotes.md @@ -3,11 +3,11 @@ ## Rule Details This rule extends the base [`eslint/quotes`](https://eslint.org/docs/rules/quotes) rule. -It supports all options and features of the base rule. +It adds support for TypeScript features which allow quoted names, but not backtick quoted names. ## How to use -```cjson +```jsonc { // note you must disable the base rule as it can report incorrect errors "quotes": "off", diff --git a/packages/eslint-plugin/docs/rules/require-await.md b/packages/eslint-plugin/docs/rules/require-await.md index 23160dc7db5..2d8ad41fda9 100644 --- a/packages/eslint-plugin/docs/rules/require-await.md +++ b/packages/eslint-plugin/docs/rules/require-await.md @@ -1,49 +1,32 @@ # Disallow async functions which have no `await` expression (`require-await`) -Asynchronous functions that don’t use `await` might not need to be asynchronous functions and could be the unintentional result of refactoring. - ## Rule Details -The `@typescript-eslint/require-await` rule extends the `require-await` rule from ESLint core, and allows for cases where the additional typing information can prevent false positives that would otherwise trigger the rule. - -One example is when a function marked as `async` returns a value that is: +This rule extends the base [`eslint/require-await`](https://eslint.org/docs/rules/require-await) rule. +It uses type information to add support for `async` functions that return a `Promise`. -1. already a promise; or -2. the result of calling another `async` function +Examples of **correct** code for this rule: -```typescript -async function numberOne(): Promise { +```ts +async function returnsPromise1() { return Promise.resolve(1); } -async function getDataFromApi(endpoint: string): Promise { - return fetch(endpoint); -} -``` - -In the above examples, the core `require-await` triggers the following warnings: - -``` -async function 'numberOne' has no 'await' expression -async function 'getDataFromApi' has no 'await' expression +const returnsPromise2 = () => returnsPromise1(); ``` -One way to resolve these errors is to remove the `async` keyword. However doing so can cause a conflict with the [`@typescript-eslint/promise-function-async`](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/promise-function-async.md) rule (if enabled), which requires any function returning a promise to be marked as `async`. +## How to use -Another way to resolve these errors is to add an `await` keyword to the return statements. However doing so can cause a conflict with the [`no-return-await`](https://eslint.org/docs/rules/no-return-await) rule (if enabled), which warns against using `return await` since the return value of an `async` function is always wrapped in `Promise.resolve` anyway. - -With the additional typing information available in TypeScript code, this extension to the `require-await` rule is able to look at the _actual_ return types of an `async` function (before being implicitly wrapped in `Promise.resolve`), and avoid the need for an `await` expression when the return value is already a promise. - -See the [ESLint documentation](https://eslint.org/docs/rules/require-await) for more details on the `require-await` rule. - -## Rule Changes - -```cjson +```jsonc { - // note you must disable the base rule as it can report incorrect errors - "require-await": "off", - "@typescript-eslint/require-await": "error" + // note you must disable the base rule as it can report incorrect errors + "require-await": "off", + "@typescript-eslint/require-await": "error" } ``` +## Options + +See [`eslint/require-await` options](https://eslint.org/docs/rules/require-await#options). + Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/require-await.md) diff --git a/packages/eslint-plugin/docs/rules/return-await.md b/packages/eslint-plugin/docs/rules/return-await.md index c00c2be5d02..1f0adc71ec1 100644 --- a/packages/eslint-plugin/docs/rules/return-await.md +++ b/packages/eslint-plugin/docs/rules/return-await.md @@ -4,95 +4,114 @@ Returning an awaited promise can make sense for better stack trace information a ## Rule Details -The `@typescript-eslint/return-await` rule specifies that awaiting a returned non-promise is never allowed. By default, the rule requires awaiting a returned promise in a `try-catch-finally` block and disallows returning an awaited promise in any other context. Optionally, the rule can require awaiting returned promises in all contexts, or disallow them in all contexts. +This rule builds on top of the [`eslint/no-return-await`](https://eslint.org/docs/rules/no-return-await) rule. +It expands upon the base rule to add support for optionally requiring `return await` in certain cases. + +## How to use + +```jsonc +{ + // note you must disable the base rule as it can report incorrect errors + "no-return-await": "off", + "@typescript-eslint/return-await": "error" +} +``` ## Options -`in-try-catch` (default): `await`-ing a returned promise is required in `try-catch-finally` blocks and disallowed elsewhere. +```ts +type Options = 'in-try-catch' | 'always' | 'never'; + +const defaultOptions: Options = 'in-try-catch'; +``` -`always`: `await`-ing a returned promise is required everywhere. +### `in-try-catch` -`never`: `await`-ing a returned promise is disallowed everywhere. +Requires that a returned promise must be `await`ed in `try-catch-finally` blocks, and disallows it elsewhere. -```typescript -// valid in-try-catch -async function validInTryCatch1() { +Examples of **incorrect** code with `in-try-catch`: + +```ts +async function invalidInTryCatch1() { try { - return await Promise.resolve('try'); + return Promise.resolve('try'); } catch (e) {} } -async function validInTryCatch2() { - return Promise.resolve('try'); +async function invalidInTryCatch2() { + return await Promise.resolve('try'); } -async function validInTryCatch3() { - return 'value'; +async function invalidInTryCatch3() { + return await 'value'; } +``` -// valid always -async function validAlways1() { +Examples of **correct** code with `in-try-catch`: + +```ts +async function validInTryCatch1() { try { return await Promise.resolve('try'); } catch (e) {} } -async function validAlways2() { - return await Promise.resolve('try'); +async function validInTryCatch2() { + return Promise.resolve('try'); } -async function validAlways3() { +async function validInTryCatch3() { return 'value'; } +``` -// valid never -async function validNever1() { +### `always` + +Requires that all returned promises are `await`ed. + +Examples of **incorrect** code with `always`: + +```ts +async function invalidAlways1() { try { return Promise.resolve('try'); } catch (e) {} } -async function validNever2() { +async function invalidAlways2() { return Promise.resolve('try'); } -async function validNever3() { - return 'value'; +async function invalidAlways3() { + return await 'value'; } ``` -```typescript -// invalid in-try-catch -async function invalidInTryCatch1() { +Examples of **correct** code with `always`: + +```ts +async function validAlways1() { try { - return Promise.resolve('try'); + return await Promise.resolve('try'); } catch (e) {} } -async function invalidInTryCatch2() { +async function validAlways2() { return await Promise.resolve('try'); } -async function invalidInTryCatch3() { - return await 'value'; +async function validAlways3() { + return 'value'; } +``` -// invalid always -async function invalidAlways1() { - try { - return Promise.resolve('try'); - } catch (e) {} -} +### `never` -async function invalidAlways2() { - return Promise.resolve('try'); -} +Disallows all `await`ing any returned promises. -async function invalidAlways3() { - return await 'value'; -} +Examples of **incorrect** code with `never`: -// invalid never +```ts async function invalidNever1() { try { return await Promise.resolve('try'); @@ -108,16 +127,20 @@ async function invalidNever3() { } ``` -The rule also applies to `finally` blocks. So the following would be invalid with default options: +Examples of **correct** code with `never`: -```typescript -async function invalid() { +```ts +async function validNever1() { try { - return await Promise.resolve('try'); - } catch (e) { - return Promise.resolve('catch'); - } finally { - // cleanup - } + return Promise.resolve('try'); + } catch (e) {} +} + +async function validNever2() { + return Promise.resolve('try'); +} + +async function validNever3() { + return 'value'; } ``` diff --git a/packages/eslint-plugin/docs/rules/semi.md b/packages/eslint-plugin/docs/rules/semi.md index bbb3154fa1a..c233d354631 100644 --- a/packages/eslint-plugin/docs/rules/semi.md +++ b/packages/eslint-plugin/docs/rules/semi.md @@ -5,15 +5,13 @@ This rule enforces consistent use of semicolons after statements. ## Rule Details This rule extends the base [`eslint/semi`](https://eslint.org/docs/rules/semi) rule. -It supports all options and features of the base rule. -This version adds support for numerous typescript features. +It adds support for TypeScript features that require semicolons. -See also the [`@typescript-eslint/member-delimiter-style`](member-delimiter-style.md) rule, -which allows you to specify the delimiter for `type` and `interface` members. +See also the [`@typescript-eslint/member-delimiter-style`](member-delimiter-style.md) rule, which allows you to specify the delimiter for `type` and `interface` members. ## How to use -```cjson +```jsonc { // note you must disable the base rule as it can report incorrect errors "semi": "off", diff --git a/packages/eslint-plugin/docs/rules/space-before-function-paren.md b/packages/eslint-plugin/docs/rules/space-before-function-paren.md index bfbcb22d887..db2579e44d1 100644 --- a/packages/eslint-plugin/docs/rules/space-before-function-paren.md +++ b/packages/eslint-plugin/docs/rules/space-before-function-paren.md @@ -1,33 +1,13 @@ # Enforces consistent spacing before function parenthesis (`space-before-function-paren`) -When formatting a function, whitespace is allowed between the function name or `function` keyword and the opening parenthesis. Named functions also require a space between the `function` keyword and the function name, but anonymous functions require no whitespace. For example: - - -```ts -function withoutSpace(x) { - // ... -} - -function withSpace (x) { - // ... -} - -var anonymousWithoutSpace = function() {}; - -var anonymousWithSpace = function () {}; -``` - -Style guides may require a space after the `function` keyword for anonymous functions, while others specify no whitespace. Similarly, the space after a function name may or may not be required. - ## Rule Details This rule extends the base [`eslint/space-before-function-paren`](https://eslint.org/docs/rules/space-before-function-paren) rule. -It supports all options and features of the base rule. -This version adds support for generic type parameters on function calls. +It adds support for generic type parameters on function calls. ## How to use -```cjson +```jsonc { // note you must disable the base rule as it can report incorrect errors "space-before-function-paren": "off", diff --git a/packages/eslint-plugin/src/rules/ban-ts-ignore.ts b/packages/eslint-plugin/src/rules/ban-ts-ignore.ts index 7551df8d022..551bb0401a9 100644 --- a/packages/eslint-plugin/src/rules/ban-ts-ignore.ts +++ b/packages/eslint-plugin/src/rules/ban-ts-ignore.ts @@ -16,7 +16,7 @@ export default util.createRule({ 'Do not use "// @ts-ignore" comments because they suppress compilation errors.', }, deprecated: true, - replacedBy: ['@typescript-eslint/ban-ts-comment'], + replacedBy: ['ban-ts-comment'], }, defaultOptions: [], create(context) { From 3eee804461d017ea6189cd7f64fcd473623684b4 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Mon, 13 Apr 2020 09:36:51 +0900 Subject: [PATCH 08/18] =?UTF-8?q?feat(eslint-plugin):=20[explicit-module-b?= =?UTF-8?q?oundary-types]=20add=20optio=E2=80=A6=20(#1778)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rules/explicit-module-boundary-types.md | 27 ++ .../rules/explicit-module-boundary-types.ts | 215 +++++++++-- .../explicit-module-boundary-types.test.ts | 355 ++++++++++++++++++ 3 files changed, 568 insertions(+), 29 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/explicit-module-boundary-types.md b/packages/eslint-plugin/docs/rules/explicit-module-boundary-types.md index a5327a1e547..3c077a75c88 100644 --- a/packages/eslint-plugin/docs/rules/explicit-module-boundary-types.md +++ b/packages/eslint-plugin/docs/rules/explicit-module-boundary-types.md @@ -86,12 +86,17 @@ type Options = { * An array of function/method names that will not have their arguments or their return values checked. */ allowedNames?: string[]; + /** + * If true, track references to exported variables as well as direct exports. + */ + shouldTrackReferences?: boolean; }; const defaults = { allowTypedFunctionExpressions: true, allowHigherOrderFunctions: true, allowedNames: [], + shouldTrackReferences: true, }; ``` @@ -238,6 +243,28 @@ You may pass function/method names you would like this rule to ignore, like so: } ``` +### `shouldTrackReferences` + +Examples of **incorrect** code for this rule with `{ shouldTrackReferences: true }`: + +```ts +function foo(bar) { + return bar; +} + +export default foo; +``` + +Examples of **correct** code for this rule with `{ shouldTrackReferences: true }`: + +```ts +function foo(bar: string): string { + return bar; +} + +export default foo; +``` + ## When Not To Use It If you wish to make sure all functions have explicit return types, as opposed to only the module boundaries, you can use [explicit-function-return-type](https://github.com/eslint/eslint/blob/master/docs/rules/explicit-function-return-type.md) diff --git a/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts b/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts index 55c6bd29f99..b00d8eb1087 100644 --- a/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts +++ b/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts @@ -1,6 +1,7 @@ import { TSESTree, AST_NODE_TYPES, + TSESLint, } from '@typescript-eslint/experimental-utils'; import * as util from '../util'; import { @@ -15,6 +16,7 @@ type Options = [ allowHigherOrderFunctions?: boolean; allowDirectConstAssertionInArrowFunctions?: boolean; allowedNames?: string[]; + shouldTrackReferences?: boolean; }, ]; type MessageIds = 'missingReturnType' | 'missingArgType'; @@ -52,6 +54,9 @@ export default util.createRule({ type: 'string', }, }, + shouldTrackReferences: { + type: 'boolean', + }, }, additionalProperties: false, }, @@ -63,6 +68,7 @@ export default util.createRule({ allowHigherOrderFunctions: true, allowDirectConstAssertionInArrowFunctions: true, allowedNames: [], + shouldTrackReferences: true, }, ], create(context, [options]) { @@ -171,50 +177,201 @@ export default util.createRule({ return false; } + /** + * Finds an array of a function expression node referred by a variable passed from parameters + */ + function findFunctionExpressionsInScope( + variable: TSESLint.Scope.Variable, + ): + | (TSESTree.FunctionExpression | TSESTree.ArrowFunctionExpression)[] + | undefined { + const writeExprs = variable.references + .map(ref => ref.writeExpr) + .filter( + ( + expr, + ): expr is + | TSESTree.ArrowFunctionExpression + | TSESTree.FunctionExpression => + expr?.type === AST_NODE_TYPES.FunctionExpression || + expr?.type === AST_NODE_TYPES.ArrowFunctionExpression, + ); + + return writeExprs; + } + + /** + * Finds a function node referred by a variable passed from parameters + */ + function findFunctionInScope( + variable: TSESLint.Scope.Variable, + ): TSESTree.FunctionDeclaration | undefined { + if (variable.defs[0].type !== 'FunctionName') { + return; + } + + const functionNode = variable.defs[0].node; + + if (functionNode?.type !== AST_NODE_TYPES.FunctionDeclaration) { + return; + } + + return functionNode; + } + + /** + * Checks if a function referred by the identifier passed from parameters follow the rule + */ + function checkWithTrackingReferences(node: TSESTree.Identifier): void { + const scope = context.getScope(); + const variable = scope.set.get(node.name); + + if (!variable) { + return; + } + + if (variable.defs[0].type === 'ClassName') { + const classNode = variable.defs[0].node; + for (const classElement of classNode.body.body) { + if ( + classElement.type === AST_NODE_TYPES.MethodDefinition && + classElement.value.type === AST_NODE_TYPES.FunctionExpression + ) { + checkFunctionExpression(classElement.value); + } + + if ( + classElement.type === AST_NODE_TYPES.ClassProperty && + (classElement.value?.type === AST_NODE_TYPES.FunctionExpression || + classElement.value?.type === + AST_NODE_TYPES.ArrowFunctionExpression) + ) { + checkFunctionExpression(classElement.value); + } + } + } + + const functionNode = findFunctionInScope(variable); + if (functionNode) { + checkFunction(functionNode); + } + + const functionExpressions = findFunctionExpressionsInScope(variable); + if (functionExpressions && functionExpressions.length > 0) { + for (const functionExpression of functionExpressions) { + checkFunctionExpression(functionExpression); + } + } + } + + /** + * Checks if a function expression follow the rule + */ + function checkFunctionExpression( + node: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression, + ): void { + if ( + node.parent?.type === AST_NODE_TYPES.MethodDefinition && + node.parent.accessibility === 'private' + ) { + // don't check private methods as they aren't part of the public signature + return; + } + + if ( + isAllowedName(node.parent) || + isTypedFunctionExpression(node, options) + ) { + return; + } + + checkFunctionExpressionReturnType(node, options, sourceCode, loc => + context.report({ + node, + loc, + messageId: 'missingReturnType', + }), + ); + + checkArguments(node); + } + + /** + * Checks if a function follow the rule + */ + function checkFunction(node: TSESTree.FunctionDeclaration): void { + if (isAllowedName(node.parent)) { + return; + } + + checkFunctionReturnType(node, options, sourceCode, loc => + context.report({ + node, + loc, + messageId: 'missingReturnType', + }), + ); + + checkArguments(node); + } + return { 'ArrowFunctionExpression, FunctionExpression'( node: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression, ): void { - if ( - node.parent?.type === AST_NODE_TYPES.MethodDefinition && - node.parent.accessibility === 'private' - ) { - // don't check private methods as they aren't part of the public signature + if (isUnexported(node)) { return; } - if ( - isAllowedName(node.parent) || - isUnexported(node) || - isTypedFunctionExpression(node, options) - ) { + checkFunctionExpression(node); + }, + FunctionDeclaration(node): void { + if (isUnexported(node)) { return; } - checkFunctionExpressionReturnType(node, options, sourceCode, loc => - context.report({ - node, - loc, - messageId: 'missingReturnType', - }), - ); - - checkArguments(node); + checkFunction(node); }, - FunctionDeclaration(node): void { - if (isAllowedName(node.parent) || isUnexported(node)) { + 'ExportDefaultDeclaration, TSExportAssignment'( + node: TSESTree.ExportDefaultDeclaration | TSESTree.TSExportAssignment, + ): void { + if (!options.shouldTrackReferences) { return; } - checkFunctionReturnType(node, options, sourceCode, loc => - context.report({ - node, - loc, - messageId: 'missingReturnType', - }), - ); + let exported: TSESTree.Node; + + if (node.type === AST_NODE_TYPES.ExportDefaultDeclaration) { + exported = node.declaration; + } else { + exported = node.expression; + } - checkArguments(node); + switch (exported.type) { + case AST_NODE_TYPES.Identifier: { + checkWithTrackingReferences(exported); + break; + } + case AST_NODE_TYPES.ArrayExpression: { + for (const element of exported.elements) { + if (element.type === AST_NODE_TYPES.Identifier) { + checkWithTrackingReferences(element); + } + } + break; + } + case AST_NODE_TYPES.ObjectExpression: { + for (const property of exported.properties) { + if ( + property.type === AST_NODE_TYPES.Property && + property.value.type === AST_NODE_TYPES.Identifier + ) { + checkWithTrackingReferences(property.value); + } + } + break; + } + } }, }; }, diff --git a/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts b/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts index cdff53533b7..5d63e8e1036 100644 --- a/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts +++ b/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts @@ -348,6 +348,95 @@ export const Foo: JSX.Element = ( ecmaFeatures: { jsx: true }, }, }, + { + code: ` +const test = (): void => { + return; +}; +export default test; + `, + options: [{ shouldTrackReferences: true }], + }, + { + code: ` +function test(): void { + return; +} +export default test; + `, + options: [{ shouldTrackReferences: true }], + }, + { + code: ` +const test = (): void => { + return; +}; +export default [test]; + `, + options: [{ shouldTrackReferences: true }], + }, + { + code: ` +function test(): void { + return; +} +export default [test]; + `, + options: [{ shouldTrackReferences: true }], + }, + { + code: ` +const test = (): void => { + return; +}; +export default { test }; + `, + options: [{ shouldTrackReferences: true }], + }, + { + code: ` +function test(): void { + return; +} +export default { test }; + `, + options: [{ shouldTrackReferences: true }], + }, + { + code: ` +const foo = (arg => arg) as Foo; +export default foo; + `, + options: [{ shouldTrackReferences: true }], + }, + { + code: ` +let foo = (arg => arg) as Foo; +foo = 3; +export default foo; + `, + options: [{ shouldTrackReferences: true }], + }, + { + code: ` +class Foo { + bar = (arg: string): string => arg; +} +export default { Foo }; + `, + options: [{ shouldTrackReferences: true }], + }, + { + code: ` +class Foo { + bar(): void { + return; + } +} +export default { Foo }; + `, + options: [{ shouldTrackReferences: true }], + }, ], invalid: [ { @@ -925,5 +1014,271 @@ export function fn(test): string { }, ], }, + { + code: ` +const foo = arg => arg; +export default foo; + `, + options: [{ shouldTrackReferences: true }], + errors: [ + { + messageId: 'missingReturnType', + line: 2, + }, + { + messageId: 'missingArgType', + line: 2, + }, + ], + }, + { + code: ` +const foo = arg => arg; +export = foo; + `, + options: [{ shouldTrackReferences: true }], + errors: [ + { + messageId: 'missingReturnType', + line: 2, + }, + { + messageId: 'missingArgType', + line: 2, + }, + ], + }, + { + code: ` +let foo = (arg: number): number => arg; +foo = arg => arg; +export default foo; + `, + options: [{ shouldTrackReferences: true }], + errors: [ + { + messageId: 'missingReturnType', + line: 3, + }, + { + messageId: 'missingArgType', + line: 3, + }, + ], + }, + { + code: ` +const foo = arg => arg; +export default [foo]; + `, + options: [{ shouldTrackReferences: true }], + errors: [ + { + messageId: 'missingReturnType', + line: 2, + }, + { + messageId: 'missingArgType', + line: 2, + }, + ], + }, + { + code: ` +const foo = arg => arg; +export default { foo }; + `, + options: [{ shouldTrackReferences: true }], + errors: [ + { + messageId: 'missingReturnType', + line: 2, + }, + { + messageId: 'missingArgType', + line: 2, + }, + ], + }, + { + code: ` +function foo(arg) { + return arg; +} +export default foo; + `, + options: [{ shouldTrackReferences: true }], + errors: [ + { + messageId: 'missingReturnType', + line: 2, + }, + { + messageId: 'missingArgType', + line: 2, + }, + ], + }, + { + code: ` +function foo(arg) { + return arg; +} +export default [foo]; + `, + options: [{ shouldTrackReferences: true }], + errors: [ + { + messageId: 'missingReturnType', + line: 2, + }, + { + messageId: 'missingArgType', + line: 2, + }, + ], + }, + { + code: ` +function foo(arg) { + return arg; +} +export default { foo }; + `, + options: [{ shouldTrackReferences: true }], + errors: [ + { + messageId: 'missingReturnType', + line: 2, + }, + { + messageId: 'missingArgType', + line: 2, + }, + ], + }, + { + code: ` +const bar = function foo(arg) { + return arg; +}; +export default { bar }; + `, + options: [{ shouldTrackReferences: true }], + errors: [ + { + messageId: 'missingReturnType', + line: 2, + }, + { + messageId: 'missingArgType', + line: 2, + }, + ], + }, + { + code: ` +class Foo { + bool(arg) { + return arg; + } +} +export default Foo; + `, + options: [{ shouldTrackReferences: true }], + errors: [ + { + messageId: 'missingReturnType', + line: 3, + }, + { + messageId: 'missingArgType', + line: 3, + }, + ], + }, + { + code: ` +class Foo { + bool = arg => { + return arg; + }; +} +export default Foo; + `, + options: [{ shouldTrackReferences: true }], + errors: [ + { + messageId: 'missingReturnType', + line: 3, + }, + { + messageId: 'missingArgType', + line: 3, + }, + ], + }, + { + code: ` +class Foo { + bool = function(arg) { + return arg; + }; +} +export default Foo; + `, + options: [{ shouldTrackReferences: true }], + errors: [ + { + messageId: 'missingReturnType', + line: 3, + }, + { + messageId: 'missingArgType', + line: 3, + }, + ], + }, + { + code: ` +class Foo { + bool = function(arg) { + return arg; + }; +} +export default [Foo]; + `, + options: [{ shouldTrackReferences: true }], + errors: [ + { + messageId: 'missingReturnType', + line: 3, + }, + { + messageId: 'missingArgType', + line: 3, + }, + ], + }, + { + code: ` +let test = arg => argl; +test = (): void => { + return; +}; +export default test; + `, + options: [{ shouldTrackReferences: true }], + errors: [ + { + messageId: 'missingReturnType', + line: 2, + }, + { + messageId: 'missingArgType', + line: 2, + }, + ], + }, ], }); From 2e9c2028a8a0b226e0f87d4bcc997fa259ca3ebd Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Mon, 13 Apr 2020 12:50:26 +1200 Subject: [PATCH 09/18] feat(eslint-plugin): [explicit-function-return-type] add option to allow concise arrows that start with void (#1732) --- .../rules/explicit-function-return-type.md | 20 ++++++++++ .../rules/explicit-function-return-type.ts | 20 +++++++++- .../explicit-function-return-type.test.ts | 39 +++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/docs/rules/explicit-function-return-type.md b/packages/eslint-plugin/docs/rules/explicit-function-return-type.md index 360fdce01e6..aa94a29a0ef 100644 --- a/packages/eslint-plugin/docs/rules/explicit-function-return-type.md +++ b/packages/eslint-plugin/docs/rules/explicit-function-return-type.md @@ -69,6 +69,8 @@ type Options = { allowTypedFunctionExpressions?: boolean; // if true, functions immediately returning another function expression will not be checked allowHigherOrderFunctions?: boolean; + // if true, concise arrow functions that start with the void keyword will not be checked + allowConciseArrowFunctionExpressionStartingWithVoid?: boolean; }; const defaults = { @@ -198,6 +200,24 @@ function fn() { } ``` +### `allowConciseArrowFunctionExpressionsStartingWithVoid` + +Examples of **incorrect** code for this rule with `{ allowConciseArrowFunctionExpressionsStartingWithVoid: true }`: + +```ts +var join = (a: string, b: string) => `${a}${b}`; + +const log = (message: string) => { + console.log(message); +}; +``` + +Examples of **correct** code for this rule with `{ allowConciseArrowFunctionExpressionsStartingWithVoid: true }`: + +```ts +var log = (message: string) => void console.log(message); +``` + ## When Not To Use It If you don't wish to prevent calling code from using function return values in unexpected ways, then diff --git a/packages/eslint-plugin/src/rules/explicit-function-return-type.ts b/packages/eslint-plugin/src/rules/explicit-function-return-type.ts index b8ee7ff584b..f0a5978d02f 100644 --- a/packages/eslint-plugin/src/rules/explicit-function-return-type.ts +++ b/packages/eslint-plugin/src/rules/explicit-function-return-type.ts @@ -1,4 +1,7 @@ -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { + AST_NODE_TYPES, + TSESTree, +} from '@typescript-eslint/experimental-utils'; import * as util from '../util'; import { checkFunctionReturnType, @@ -11,6 +14,7 @@ type Options = [ allowTypedFunctionExpressions?: boolean; allowHigherOrderFunctions?: boolean; allowDirectConstAssertionInArrowFunctions?: boolean; + allowConciseArrowFunctionExpressionsStartingWithVoid?: boolean; }, ]; type MessageIds = 'missingReturnType'; @@ -44,6 +48,9 @@ export default util.createRule({ allowDirectConstAssertionInArrowFunctions: { type: 'boolean', }, + allowConciseArrowFunctionExpressionsStartingWithVoid: { + type: 'boolean', + }, }, additionalProperties: false, }, @@ -55,6 +62,7 @@ export default util.createRule({ allowTypedFunctionExpressions: true, allowHigherOrderFunctions: true, allowDirectConstAssertionInArrowFunctions: true, + allowConciseArrowFunctionExpressionsStartingWithVoid: false, }, ], create(context, [options]) { @@ -64,6 +72,16 @@ export default util.createRule({ 'ArrowFunctionExpression, FunctionExpression'( node: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression, ): void { + if ( + options.allowConciseArrowFunctionExpressionsStartingWithVoid && + node.type === AST_NODE_TYPES.ArrowFunctionExpression && + node.expression && + node.body.type === AST_NODE_TYPES.UnaryExpression && + node.body.operator === 'void' + ) { + return; + } + checkFunctionExpressionReturnType(node, options, sourceCode, loc => context.report({ node, diff --git a/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts b/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts index 46ab4944185..dd3e3815676 100644 --- a/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts +++ b/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts @@ -366,6 +366,11 @@ new Foo(1, () => {}); }, ], }, + { + filename: 'test.ts', + code: 'const log = (message: string) => void console.log(message);', + options: [{ allowConciseArrowFunctionExpressionsStartingWithVoid: true }], + }, ], invalid: [ { @@ -1037,5 +1042,39 @@ const func = (value: number) => ({ type: 'X', value } as const); }, ], }, + { + filename: 'test.ts', + code: 'const log = (message: string) => void console.log(message);', + options: [ + { allowConciseArrowFunctionExpressionsStartingWithVoid: false }, + ], + errors: [ + { + messageId: 'missingReturnType', + line: 1, + endLine: 1, + column: 13, + endColumn: 33, + }, + ], + }, + { + filename: 'test.ts', + code: ` + const log = (message: string) => { + void console.log(message); + }; + `, + options: [{ allowConciseArrowFunctionExpressionsStartingWithVoid: true }], + errors: [ + { + messageId: 'missingReturnType', + line: 2, + endLine: 2, + column: 21, + endColumn: 41, + }, + ], + }, ], }); From c92d240e49113779053eac32038382b282812afc Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Mon, 13 Apr 2020 12:51:45 +1200 Subject: [PATCH 10/18] feat(eslint-plugin): add rule `prefer-reduce-type-parameter` (#1707) --- packages/eslint-plugin/README.md | 1 + .../rules/prefer-reduce-type-parameter.md | 54 +++++ packages/eslint-plugin/src/configs/all.json | 1 + packages/eslint-plugin/src/rules/index.ts | 2 + .../src/rules/prefer-reduce-type-parameter.ts | 104 ++++++++++ .../eslint-plugin/tests/fixtures/class.ts | 5 + .../prefer-reduce-type-parameter.test.ts | 191 ++++++++++++++++++ .../src/eslint-utils/deepMerge.ts | 4 +- 8 files changed, 360 insertions(+), 2 deletions(-) create mode 100644 packages/eslint-plugin/docs/rules/prefer-reduce-type-parameter.md create mode 100644 packages/eslint-plugin/src/rules/prefer-reduce-type-parameter.ts create mode 100644 packages/eslint-plugin/tests/rules/prefer-reduce-type-parameter.test.ts diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 809aac848a6..d943ffdcec9 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -149,6 +149,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int | [`@typescript-eslint/prefer-optional-chain`](./docs/rules/prefer-optional-chain.md) | Prefer using concise optional chain expressions instead of chained logical ands | | :wrench: | | | [`@typescript-eslint/prefer-readonly`](./docs/rules/prefer-readonly.md) | Requires that private members are marked as `readonly` if they're never modified outside of the constructor | | :wrench: | :thought_balloon: | | [`@typescript-eslint/prefer-readonly-parameter-types`](./docs/rules/prefer-readonly-parameter-types.md) | Requires that function parameters are typed as readonly to prevent accidental mutation of inputs | | | :thought_balloon: | +| [`@typescript-eslint/prefer-reduce-type-parameter`](./docs/rules/prefer-reduce-type-parameter.md) | Prefer using type parameter when calling `Array#reduce` instead of casting | | :wrench: | :thought_balloon: | | [`@typescript-eslint/prefer-regexp-exec`](./docs/rules/prefer-regexp-exec.md) | Enforce that `RegExp#exec` is used instead of `String#match` if no global flag is provided | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/prefer-string-starts-ends-with`](./docs/rules/prefer-string-starts-ends-with.md) | Enforce the use of `String#startsWith` and `String#endsWith` instead of other equivalent methods of checking substrings | :heavy_check_mark: | :wrench: | :thought_balloon: | | [`@typescript-eslint/promise-function-async`](./docs/rules/promise-function-async.md) | Requires any function or method that returns a Promise to be marked async | | | :thought_balloon: | diff --git a/packages/eslint-plugin/docs/rules/prefer-reduce-type-parameter.md b/packages/eslint-plugin/docs/rules/prefer-reduce-type-parameter.md new file mode 100644 index 00000000000..d6f68021cc7 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/prefer-reduce-type-parameter.md @@ -0,0 +1,54 @@ +# Prefer using type parameter when calling `Array#reduce` instead of casting (`prefer-reduce-type-parameter`) + +It's common to call `Array#reduce` with a generic type, such as an array or object, as the initial value. +Since these values are empty, their types are not usable: + +- `[]` has type `never[]`, which can't have items pushed into it as nothing is type `never` +- `{}` has type `{}`, which doesn't have an index signature and so can't have properties added to it + +A common solution to this problem is to cast the initial value. While this will work, it's not the most optimal +solution as casting has subtle effects on the underlying types that can allow bugs to slip in. + +A better (and lesser known) solution is to pass the type in as a generic parameter to `Array#reduce` explicitly. +This means that TypeScript doesn't have to try to infer the type, and avoids the common pitfalls that come with casting. + +## Rule Details + +This rule looks for calls to `Array#reduce`, and warns if an initial value is being passed & casted, +suggesting instead to pass the cast type to `Array#reduce` as its generic parameter. + +Examples of **incorrect** code for this rule: + +```ts +[1, 2, 3].reduce((arr, num) => arr.concat(num * 2), [] as number[]); + +['a', 'b'].reduce( + (accum, name) => ({ + ...accum, + [name]: true, + }), + {} as Record, +); +``` + +Examples of **correct** code for this rule: + +```ts +[1, 2, 3].reduce((arr, num) => arr.concat(num * 2), []); + +['a', 'b'].reduce>( + (accum, name) => ({ + ...accum, + [name]: true, + }), + {}, +); +``` + +## Options + +There are no options. + +## When Not To Use It + +If you don't want to use typechecking in your linting, you can't use this rule. diff --git a/packages/eslint-plugin/src/configs/all.json b/packages/eslint-plugin/src/configs/all.json index 85cf2e1ecc6..dc1ec0490ca 100644 --- a/packages/eslint-plugin/src/configs/all.json +++ b/packages/eslint-plugin/src/configs/all.json @@ -86,6 +86,7 @@ "@typescript-eslint/prefer-optional-chain": "error", "@typescript-eslint/prefer-readonly": "error", "@typescript-eslint/prefer-readonly-parameter-types": "error", + "@typescript-eslint/prefer-reduce-type-parameter": "error", "@typescript-eslint/prefer-regexp-exec": "error", "@typescript-eslint/prefer-string-starts-ends-with": "error", "@typescript-eslint/promise-function-async": "error", diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index 15ce8dd1a82..e941f5148c3 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -75,6 +75,7 @@ import preferNullishCoalescing from './prefer-nullish-coalescing'; import preferOptionalChain from './prefer-optional-chain'; import preferReadonly from './prefer-readonly'; import preferReadonlyParameterTypes from './prefer-readonly-parameter-types'; +import preferReduceTypeParameter from './prefer-reduce-type-parameter'; import preferRegexpExec from './prefer-regexp-exec'; import preferStringStartsEndsWith from './prefer-string-starts-ends-with'; import promiseFunctionAsync from './promise-function-async'; @@ -172,6 +173,7 @@ export default { 'prefer-optional-chain': preferOptionalChain, 'prefer-readonly-parameter-types': preferReadonlyParameterTypes, 'prefer-readonly': preferReadonly, + 'prefer-reduce-type-parameter': preferReduceTypeParameter, 'prefer-regexp-exec': preferRegexpExec, 'prefer-string-starts-ends-with': preferStringStartsEndsWith, 'promise-function-async': promiseFunctionAsync, diff --git a/packages/eslint-plugin/src/rules/prefer-reduce-type-parameter.ts b/packages/eslint-plugin/src/rules/prefer-reduce-type-parameter.ts new file mode 100644 index 00000000000..7b63125e743 --- /dev/null +++ b/packages/eslint-plugin/src/rules/prefer-reduce-type-parameter.ts @@ -0,0 +1,104 @@ +import { + AST_NODE_TYPES, + TSESTree, +} from '@typescript-eslint/experimental-utils'; +import * as util from '../util'; + +type MemberExpressionWithCallExpressionParent = ( + | TSESTree.MemberExpression + | TSESTree.OptionalMemberExpression +) & { parent: TSESTree.CallExpression | TSESTree.OptionalCallExpression }; + +const getMemberExpressionName = ( + member: TSESTree.MemberExpression | TSESTree.OptionalMemberExpression, +): string | null => { + if (!member.computed) { + return member.property.name; + } + + if ( + member.property.type === AST_NODE_TYPES.Literal && + typeof member.property.value === 'string' + ) { + return member.property.value; + } + + return null; +}; + +export default util.createRule({ + name: 'prefer-reduce-type-parameter', + meta: { + type: 'problem', + docs: { + category: 'Best Practices', + recommended: false, + description: + 'Prefer using type parameter when calling `Array#reduce` instead of casting', + requiresTypeChecking: true, + }, + messages: { + preferTypeParameter: + 'Unnecessary cast: Array#reduce accepts a type parameter for the default value.', + }, + fixable: 'code', + schema: [], + }, + defaultOptions: [], + create(context) { + const service = util.getParserServices(context); + const checker = service.program.getTypeChecker(); + + return { + ':matches(CallExpression, OptionalCallExpression) > :matches(MemberExpression, OptionalMemberExpression).callee'( + callee: MemberExpressionWithCallExpressionParent, + ): void { + if (getMemberExpressionName(callee) !== 'reduce') { + return; + } + + const [, secondArg] = callee.parent.arguments; + + if ( + callee.parent.arguments.length < 2 || + !util.isTypeAssertion(secondArg) + ) { + return; + } + + // Get the symbol of the `reduce` method. + const tsNode = service.esTreeNodeToTSNodeMap.get(callee.object); + const calleeObjType = util.getConstrainedTypeAtLocation( + checker, + tsNode, + ); + + // Check the owner type of the `reduce` method. + if (checker.isArrayType(calleeObjType)) { + context.report({ + messageId: 'preferTypeParameter', + node: secondArg, + fix: fixer => [ + fixer.removeRange([ + secondArg.range[0], + secondArg.expression.range[0], + ]), + fixer.removeRange([ + secondArg.expression.range[1], + secondArg.range[1], + ]), + fixer.insertTextAfter( + callee, + `<${context + .getSourceCode() + .getText(secondArg.typeAnnotation)}>`, + ), + ], + }); + + return; + } + }, + }; + }, +}); diff --git a/packages/eslint-plugin/tests/fixtures/class.ts b/packages/eslint-plugin/tests/fixtures/class.ts index 74f222338ff..044e44e44ae 100644 --- a/packages/eslint-plugin/tests/fixtures/class.ts +++ b/packages/eslint-plugin/tests/fixtures/class.ts @@ -3,3 +3,8 @@ export class Error {} // used by unbound-method test case to test imports export const console = { log() {} }; + +// used by prefer-reduce-type-parameter to test native vs userland check +export class Reducable { + reduce() {} +} diff --git a/packages/eslint-plugin/tests/rules/prefer-reduce-type-parameter.test.ts b/packages/eslint-plugin/tests/rules/prefer-reduce-type-parameter.test.ts new file mode 100644 index 00000000000..a683a4ab0dc --- /dev/null +++ b/packages/eslint-plugin/tests/rules/prefer-reduce-type-parameter.test.ts @@ -0,0 +1,191 @@ +import rule from '../../src/rules/prefer-reduce-type-parameter'; +import { RuleTester, getFixturesRootDir } from '../RuleTester'; + +const rootPath = getFixturesRootDir(); + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', + parserOptions: { + sourceType: 'module', + tsconfigRootDir: rootPath, + project: './tsconfig.json', + }, +}); + +ruleTester.run('prefer-reduce-type-parameter', rule, { + valid: [ + ` + new (class Mine { + reduce() {} + })().reduce(() => {}, 1 as any); + `, + ` + class Mine { + reduce() {} + } + + new Mine().reduce(() => {}, 1 as any); + `, + ` + import { Reducable } from './class'; + + new Reducable().reduce(() => {}, 1 as any); + `, + "[1, 2, 3]['reduce']((sum, num) => sum + num, 0);", + '[1, 2, 3][null]((sum, num) => sum + num, 0);', + '[1, 2, 3]?.[null]((sum, num) => sum + num, 0);', + '[1, 2, 3].reduce((sum, num) => sum + num, 0);', + '[1, 2, 3].reduce((a, s) => a.concat(s * 2), []);', + '[1, 2, 3]?.reduce((a, s) => a.concat(s * 2), []);', + ], + invalid: [ + { + code: '[1, 2, 3].reduce((a, s) => a.concat(s * 2), [] as number[]);', + output: '[1, 2, 3].reduce((a, s) => a.concat(s * 2), []);', + errors: [ + { + messageId: 'preferTypeParameter', + column: 45, + line: 1, + }, + ], + }, + { + code: '[1, 2, 3].reduce((a, s) => a.concat(s * 2), []);', + output: '[1, 2, 3].reduce((a, s) => a.concat(s * 2), []);', + errors: [ + { + messageId: 'preferTypeParameter', + column: 45, + line: 1, + }, + ], + }, + { + code: '[1, 2, 3]?.reduce((a, s) => a.concat(s * 2), [] as number[]);', + output: '[1, 2, 3]?.reduce((a, s) => a.concat(s * 2), []);', + errors: [ + { + messageId: 'preferTypeParameter', + column: 46, + line: 1, + }, + ], + }, + { + code: '[1, 2, 3]?.reduce((a, s) => a.concat(s * 2), []);', + output: '[1, 2, 3]?.reduce((a, s) => a.concat(s * 2), []);', + errors: [ + { + messageId: 'preferTypeParameter', + column: 46, + line: 1, + }, + ], + }, + { + code: ` +const names = ['a', 'b', 'c']; + +names.reduce( + (accum, name) => ({ + ...accum, + [name]: true, + }), + {} as Record, +); + `, + output: ` +const names = ['a', 'b', 'c']; + +names.reduce>( + (accum, name) => ({ + ...accum, + [name]: true, + }), + {}, +); + `, + errors: [ + { + messageId: 'preferTypeParameter', + column: 3, + line: 9, + }, + ], + }, + { + code: ` +['a', 'b'].reduce( + (accum, name) => ({ + ...accum, + [name]: true, + }), + >{}, +); + `, + output: ` +['a', 'b'].reduce>( + (accum, name) => ({ + ...accum, + [name]: true, + }), + {}, +); + `, + errors: [ + { + messageId: 'preferTypeParameter', + column: 3, + line: 7, + }, + ], + }, + { + code: ` +['a', 'b']['reduce']( + (accum, name) => ({ + ...accum, + [name]: true, + }), + {} as Record, +); + `, + output: ` +['a', 'b']['reduce']>( + (accum, name) => ({ + ...accum, + [name]: true, + }), + {}, +); + `, + errors: [ + { + messageId: 'preferTypeParameter', + column: 3, + line: 7, + }, + ], + }, + { + code: ` +function f(a: U) { + return a.reduce(() => {}, {} as Record); +} + `, + output: ` +function f(a: U) { + return a.reduce>(() => {}, {}); +} + `, + errors: [ + { + messageId: 'preferTypeParameter', + column: 29, + line: 3, + }, + ], + }, + ], +}); diff --git a/packages/experimental-utils/src/eslint-utils/deepMerge.ts b/packages/experimental-utils/src/eslint-utils/deepMerge.ts index 5ac6509203f..9b7baee5ebb 100644 --- a/packages/experimental-utils/src/eslint-utils/deepMerge.ts +++ b/packages/experimental-utils/src/eslint-utils/deepMerge.ts @@ -25,7 +25,7 @@ export function deepMerge( // get the unique set of keys across both objects const keys = new Set(Object.keys(first).concat(Object.keys(second))); - return Array.from(keys).reduce((acc, key) => { + return Array.from(keys).reduce((acc, key) => { const firstHasKey = key in first; const secondHasKey = key in second; const firstValue = first[key]; @@ -46,7 +46,7 @@ export function deepMerge( } return acc; - }, {} as ObjectLike); + }, {}); } export { isObjectNotArray }; From 05030f8d2bd5a50e95053bc61380891da71cc567 Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Mon, 13 Apr 2020 12:54:59 +1200 Subject: [PATCH 11/18] fix(eslint-plugin): use `isTypeArrayTypeOrUnionOfArrayTypes` util for checking if type is array (#1728) --- .../src/rules/no-for-in-array.ts | 8 ++-- .../eslint-plugin/src/rules/no-unsafe-call.ts | 3 +- .../src/rules/no-unsafe-return.ts | 3 +- .../src/rules/require-array-sort-compare.ts | 27 ++++------- .../src/rules/restrict-plus-operands.ts | 12 +---- .../rules/restrict-template-expressions.ts | 11 +---- packages/eslint-plugin/src/util/types.ts | 17 +++++++ .../tests/rules/no-for-in-array.test.ts | 45 +++++++++++++++++++ .../tests/rules/no-unsafe-call.test.ts | 7 +++ .../tests/rules/no-unsafe-return.test.ts | 18 ++++++++ 10 files changed, 106 insertions(+), 45 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-for-in-array.ts b/packages/eslint-plugin/src/rules/no-for-in-array.ts index 511b744ee4c..f89d2ffd08d 100644 --- a/packages/eslint-plugin/src/rules/no-for-in-array.ts +++ b/packages/eslint-plugin/src/rules/no-for-in-array.ts @@ -25,11 +25,13 @@ export default util.createRule({ const checker = parserServices.program.getTypeChecker(); const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node); - const type = checker.getTypeAtLocation(originalNode.expression); + const type = util.getConstrainedTypeAtLocation( + checker, + originalNode.expression, + ); if ( - (typeof type.symbol !== 'undefined' && - type.symbol.name === 'Array') || + util.isTypeArrayTypeOrUnionOfArrayTypes(type, checker) || (type.flags & ts.TypeFlags.StringLike) !== 0 ) { context.report({ diff --git a/packages/eslint-plugin/src/rules/no-unsafe-call.ts b/packages/eslint-plugin/src/rules/no-unsafe-call.ts index 66f66448c28..3661bd44f16 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-call.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-call.ts @@ -31,7 +31,8 @@ export default util.createRule<[], MessageIds>({ messageId: MessageIds, ): void { const tsNode = esTreeNodeToTSNodeMap.get(node); - const type = checker.getTypeAtLocation(tsNode); + const type = util.getConstrainedTypeAtLocation(checker, tsNode); + if (util.isTypeAnyType(type)) { context.report({ node: reportingNode, diff --git a/packages/eslint-plugin/src/rules/no-unsafe-return.ts b/packages/eslint-plugin/src/rules/no-unsafe-return.ts index fef04cd6365..5fbbfac9b90 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-return.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-return.ts @@ -74,7 +74,8 @@ export default util.createRule({ } // function has an explicit return type, so ensure it's a safe return - const returnNodeType = checker.getTypeAtLocation( + const returnNodeType = util.getConstrainedTypeAtLocation( + checker, esTreeNodeToTSNodeMap.get(returnNode), ); const functionTSNode = esTreeNodeToTSNodeMap.get(functionNode); diff --git a/packages/eslint-plugin/src/rules/require-array-sort-compare.ts b/packages/eslint-plugin/src/rules/require-array-sort-compare.ts index 25c4c2e5747..2f388d364dd 100644 --- a/packages/eslint-plugin/src/rules/require-array-sort-compare.ts +++ b/packages/eslint-plugin/src/rules/require-array-sort-compare.ts @@ -1,5 +1,4 @@ import { TSESTree } from '@typescript-eslint/experimental-utils'; -import * as ts from 'typescript'; import * as util from '../util'; export default util.createRule({ @@ -27,26 +26,16 @@ export default util.createRule({ return { ":matches(CallExpression, OptionalCallExpression)[arguments.length=0] > :matches(MemberExpression, OptionalMemberExpression)[property.name='sort'][computed=false]"( - node: TSESTree.MemberExpression | TSESTree.OptionalMemberExpression, + callee: TSESTree.MemberExpression | TSESTree.OptionalMemberExpression, ): void { - // Get the symbol of the `sort` method. - const tsNode = service.esTreeNodeToTSNodeMap.get(node); - const sortSymbol = checker.getSymbolAtLocation(tsNode); - if (sortSymbol == null) { - return; - } + const tsNode = service.esTreeNodeToTSNodeMap.get(callee.object); + const calleeObjType = util.getConstrainedTypeAtLocation( + checker, + tsNode, + ); - // Check the owner type of the `sort` method. - for (const methodDecl of sortSymbol.declarations) { - const typeDecl = methodDecl.parent; - if ( - ts.isInterfaceDeclaration(typeDecl) && - ts.isSourceFile(typeDecl.parent) && - typeDecl.name.escapedText === 'Array' - ) { - context.report({ node: node.parent!, messageId: 'requireCompare' }); - return; - } + if (util.isTypeArrayTypeOrUnionOfArrayTypes(calleeObjType, checker)) { + context.report({ node: callee.parent!, messageId: 'requireCompare' }); } }, }; diff --git a/packages/eslint-plugin/src/rules/restrict-plus-operands.ts b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts index a833825a207..2702009046f 100644 --- a/packages/eslint-plugin/src/rules/restrict-plus-operands.ts +++ b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts @@ -54,16 +54,6 @@ export default util.createRule({ * Helper function to get base type of node */ function getBaseTypeOfLiteralType(type: ts.Type): BaseLiteral { - const constraint = type.getConstraint(); - if ( - constraint && - // for generic types with union constraints, it will return itself from getConstraint - // so we have to guard against infinite recursion... - constraint !== type - ) { - return getBaseTypeOfLiteralType(constraint); - } - if (type.isNumberLiteral()) { return 'number'; } @@ -98,7 +88,7 @@ export default util.createRule({ */ function getNodeType(node: TSESTree.Expression): BaseLiteral { const tsNode = service.esTreeNodeToTSNodeMap.get(node); - const type = typeChecker.getTypeAtLocation(tsNode); + const type = util.getConstrainedTypeAtLocation(typeChecker, tsNode); return getBaseTypeOfLiteralType(type); } diff --git a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts index 63b257ffe7f..d73d8d98a25 100644 --- a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts +++ b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts @@ -98,21 +98,12 @@ export default util.createRule({ */ function getNodeType(node: TSESTree.Expression): BaseType[] { const tsNode = service.esTreeNodeToTSNodeMap.get(node); - const type = typeChecker.getTypeAtLocation(tsNode); + const type = util.getConstrainedTypeAtLocation(typeChecker, tsNode); return getBaseType(type); } function getBaseType(type: ts.Type): BaseType[] { - const constraint = type.getConstraint(); - if ( - constraint && - // for generic types with union constraints, it will return itself - constraint !== type - ) { - return getBaseType(constraint); - } - if (type.isStringLiteral()) { return ['string']; } diff --git a/packages/eslint-plugin/src/util/types.ts b/packages/eslint-plugin/src/util/types.ts index ae6ee3843aa..261c154361b 100644 --- a/packages/eslint-plugin/src/util/types.ts +++ b/packages/eslint-plugin/src/util/types.ts @@ -13,6 +13,23 @@ import { } from 'tsutils'; import * as ts from 'typescript'; +/** + * Checks if the given type is either an array type, + * or a union made up solely of array types. + */ +export function isTypeArrayTypeOrUnionOfArrayTypes( + type: ts.Type, + checker: ts.TypeChecker, +): boolean { + for (const t of unionTypeParts(type)) { + if (!checker.isArrayType(t)) { + return false; + } + } + + return true; +} + /** * @param type Type being checked by name. * @param allowedNames Symbol names checking on the type. diff --git a/packages/eslint-plugin/tests/rules/no-for-in-array.test.ts b/packages/eslint-plugin/tests/rules/no-for-in-array.test.ts index 134fd513e87..7c3ec309185 100644 --- a/packages/eslint-plugin/tests/rules/no-for-in-array.test.ts +++ b/packages/eslint-plugin/tests/rules/no-for-in-array.test.ts @@ -54,5 +54,50 @@ for (const x in z) { }, ], }, + { + code: ` +const fn = (arr: number[]) => { + for (const x in arr) { + console.log(x); + } +}; + `, + errors: [ + { + messageId: 'forInViolation', + type: AST_NODE_TYPES.ForInStatement, + }, + ], + }, + { + code: ` +const fn = (arr: number[] | string[]) => { + for (const x in arr) { + console.log(x); + } +}; + `, + errors: [ + { + messageId: 'forInViolation', + type: AST_NODE_TYPES.ForInStatement, + }, + ], + }, + { + code: ` +const fn = (arr: T) => { + for (const x in arr) { + console.log(x); + } +}; + `, + errors: [ + { + messageId: 'forInViolation', + type: AST_NODE_TYPES.ForInStatement, + }, + ], + }, ], }); diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts index 8adb44b9960..f860410da5c 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts @@ -47,6 +47,7 @@ function foo(x: any) { x() } function foo(x: any) { x?.() } function foo(x: any) { x.a.b.c.d.e.f.g() } function foo(x: any) { x.a.b.c.d.e.f.g?.() } +function foo(x: T) { x() } `, errors: [ { @@ -73,6 +74,12 @@ function foo(x: any) { x.a.b.c.d.e.f.g?.() } column: 24, endColumn: 39, }, + { + messageId: 'unsafeCall', + line: 6, + column: 37, + endColumn: 38, + }, ], }), ...batchedSingleLineTests({ diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts index b0c1ead9007..42a58734a47 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts @@ -73,6 +73,24 @@ function foo(): Set { `, ], invalid: [ + { + code: ` +function fn(x: T) { + return x; +} + `, + errors: [ + { + messageId: 'unsafeReturnAssignment', + data: { + sender: 'any', + receiver: 'T', + }, + line: 3, + column: 3, + }, + ], + }, ...batchedSingleLineTests({ code: noFormat` function foo() { return (1 as any); } From 469cff332c041f38f60de052769287342455cff1 Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Mon, 13 Apr 2020 13:02:00 +1200 Subject: [PATCH 12/18] feat(eslint-plugin): [ban-ts-comment] support `ts-expect-error` (#1706) --- .../docs/rules/ban-ts-comment.md | 5 +- .../eslint-plugin/src/rules/ban-ts-comment.ts | 8 +- .../tests/rules/ban-ts-comment.test.ts | 74 ++++++++++++++++++- 3 files changed, 84 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/ban-ts-comment.md b/packages/eslint-plugin/docs/rules/ban-ts-comment.md index 58f99dc048a..b1d93409c28 100644 --- a/packages/eslint-plugin/docs/rules/ban-ts-comment.md +++ b/packages/eslint-plugin/docs/rules/ban-ts-comment.md @@ -6,6 +6,7 @@ Using these to suppress TypeScript Compiler Errors reduces the effectiveness of The directive comments supported by TypeScript are: ``` +// @ts-expect-error // @ts-ignore // @ts-nocheck // @ts-check @@ -14,18 +15,20 @@ The directive comments supported by TypeScript are: ## Rule Details This rule lets you set which directive comments you want to allow in your codebase. -By default, only `@ts-check` is allowed, as it enables rather then suppresses errors. +By default, only `@ts-check` is allowed, as it enables rather than suppresses errors. The configuration looks like this: ``` interface Options { + 'ts-expect-error'?: boolean; 'ts-ignore'?: boolean; 'ts-nocheck'?: boolean; 'ts-check'?: boolean; } const defaultOptions: Options = { + 'ts-expect-error': true, 'ts-ignore': true, 'ts-nocheck': true, 'ts-check': false diff --git a/packages/eslint-plugin/src/rules/ban-ts-comment.ts b/packages/eslint-plugin/src/rules/ban-ts-comment.ts index 53505cfea22..a422349eefa 100644 --- a/packages/eslint-plugin/src/rules/ban-ts-comment.ts +++ b/packages/eslint-plugin/src/rules/ban-ts-comment.ts @@ -2,6 +2,7 @@ import { AST_TOKEN_TYPES } from '@typescript-eslint/experimental-utils'; import * as util from '../util'; interface Options { + 'ts-expect-error'?: boolean; 'ts-ignore'?: boolean; 'ts-nocheck'?: boolean; 'ts-check'?: boolean; @@ -9,6 +10,7 @@ interface Options { const defaultOptions: [Options] = [ { + 'ts-expect-error': true, 'ts-ignore': true, 'ts-nocheck': true, 'ts-check': false, @@ -34,6 +36,10 @@ export default util.createRule<[Options], MessageIds>({ { type: 'object', properties: { + 'ts-expect-error': { + type: 'boolean', + default: true, + }, 'ts-ignore': { type: 'boolean', default: true, @@ -53,7 +59,7 @@ export default util.createRule<[Options], MessageIds>({ }, defaultOptions, create(context, [options]) { - const tsCommentRegExp = /^\/*\s*@ts-(ignore|check|nocheck)/; + const tsCommentRegExp = /^\/*\s*@ts-(expect-error|ignore|check|nocheck)/; const sourceCode = context.getSourceCode(); return { diff --git a/packages/eslint-plugin/tests/rules/ban-ts-comment.test.ts b/packages/eslint-plugin/tests/rules/ban-ts-comment.test.ts index fcbb788a308..89ff1db4a61 100644 --- a/packages/eslint-plugin/tests/rules/ban-ts-comment.test.ts +++ b/packages/eslint-plugin/tests/rules/ban-ts-comment.test.ts @@ -5,7 +5,79 @@ const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', }); -ruleTester.run('ban-ts-comment', rule, { +ruleTester.run('ts-expect-error', rule, { + valid: [ + '// just a comment containing @ts-expect-error somewhere', + '/* @ts-expect-error */', + '/** @ts-expect-error */', + ` +/* +// @ts-expect-error in a block +*/ + `, + { + code: '// @ts-expect-error', + options: [{ 'ts-expect-error': false }], + }, + ], + invalid: [ + { + code: '// @ts-expect-error', + options: [{ 'ts-expect-error': true }], + errors: [ + { + data: { directive: 'expect-error' }, + messageId: 'tsDirectiveComment', + line: 1, + column: 1, + }, + ], + }, + { + code: '// @ts-expect-error: Suppress next line', + options: [{ 'ts-expect-error': true }], + errors: [ + { + data: { directive: 'expect-error' }, + messageId: 'tsDirectiveComment', + line: 1, + column: 1, + }, + ], + }, + { + code: '/////@ts-expect-error: Suppress next line', + options: [{ 'ts-expect-error': true }], + errors: [ + { + data: { directive: 'expect-error' }, + messageId: 'tsDirectiveComment', + line: 1, + column: 1, + }, + ], + }, + { + code: ` +if (false) { + // @ts-expect-error: Unreachable code error + console.log('hello'); +} + `, + options: [{ 'ts-expect-error': true }], + errors: [ + { + data: { directive: 'expect-error' }, + messageId: 'tsDirectiveComment', + line: 3, + column: 3, + }, + ], + }, + ], +}); + +ruleTester.run('ts-ignore', rule, { valid: [ '// just a comment containing @ts-ignore somewhere', '/* @ts-ignore */', From 7021f2151a25db2a8edf17e06cd6f21e90761ec8 Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Mon, 13 Apr 2020 13:11:37 +1200 Subject: [PATCH 13/18] feat(eslint-plugin): add rule `prefer-ts-expect-error` (#1705) --- packages/eslint-plugin/README.md | 1 + .../docs/rules/prefer-ts-expect-error.md | 47 ++++++++++ packages/eslint-plugin/src/configs/all.json | 1 + packages/eslint-plugin/src/rules/index.ts | 2 + .../src/rules/prefer-ts-expect-error.ts | 55 ++++++++++++ .../rules/prefer-ts-expect-error.test.ts | 85 +++++++++++++++++++ .../src/ts-estree/ts-estree.ts | 1 + 7 files changed, 192 insertions(+) create mode 100644 packages/eslint-plugin/docs/rules/prefer-ts-expect-error.md create mode 100644 packages/eslint-plugin/src/rules/prefer-ts-expect-error.ts create mode 100644 packages/eslint-plugin/tests/rules/prefer-ts-expect-error.test.ts diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index d943ffdcec9..7ed22981926 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -152,6 +152,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int | [`@typescript-eslint/prefer-reduce-type-parameter`](./docs/rules/prefer-reduce-type-parameter.md) | Prefer using type parameter when calling `Array#reduce` instead of casting | | :wrench: | :thought_balloon: | | [`@typescript-eslint/prefer-regexp-exec`](./docs/rules/prefer-regexp-exec.md) | Enforce that `RegExp#exec` is used instead of `String#match` if no global flag is provided | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/prefer-string-starts-ends-with`](./docs/rules/prefer-string-starts-ends-with.md) | Enforce the use of `String#startsWith` and `String#endsWith` instead of other equivalent methods of checking substrings | :heavy_check_mark: | :wrench: | :thought_balloon: | +| [`@typescript-eslint/prefer-ts-expect-error`](./docs/rules/prefer-ts-expect-error.md) | Recommends using `// @ts-expect-error` over `// @ts-ignore` | | :wrench: | | | [`@typescript-eslint/promise-function-async`](./docs/rules/promise-function-async.md) | Requires any function or method that returns a Promise to be marked async | | | :thought_balloon: | | [`@typescript-eslint/require-array-sort-compare`](./docs/rules/require-array-sort-compare.md) | Requires `Array#sort` calls to always provide a `compareFunction` | | | :thought_balloon: | | [`@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 | | | :thought_balloon: | diff --git a/packages/eslint-plugin/docs/rules/prefer-ts-expect-error.md b/packages/eslint-plugin/docs/rules/prefer-ts-expect-error.md new file mode 100644 index 00000000000..7ec0cac771b --- /dev/null +++ b/packages/eslint-plugin/docs/rules/prefer-ts-expect-error.md @@ -0,0 +1,47 @@ +# Recommends using `// @ts-expect-error` over `// @ts-ignore` (`prefer-ts-expect-error`) + +TypeScript allows you to suppress all errors on a line by placing a single-line comment starting with `@ts-ignore` immediately before the erroring line. +While powerful, there is no way to know if a `@ts-ignore` is actually suppressing an error without manually investigating what happens when the `@ts-ignore` is removed. + +This means its easy for `@ts-ignore`s to be forgotten about, and remain in code even after the error they were suppressing is fixed. +This is dangerous, as if a new error arises on that line it'll be suppressed by the forgotten about `@ts-ignore`, and so be missed. + +To address this, TS3.9 ships with a new single-line comment directive: `// @ts-expect-error`. + +This directive operates in the same manner as `@ts-ignore`, but will error if the line it's meant to be suppressing doesn't actually contain an error, making it a lot safer. + +## Rule Details + +This rule looks for usages of `@ts-ignore`, and flags them to be replaced with `@ts-expect-error`. + +Examples of **incorrect** code for this rule: + +```ts +// @ts-ignore +const str: string = 1; + +const isOptionEnabled = (key: string): boolean => { + // @ts-ignore: if key isn't in globalOptions it'll be undefined which is false + return !!globalOptions[key]; +}; +``` + +Examples of **correct** code for this rule: + +```ts +// @ts-expect-error +const str: string = 1; + +const isOptionEnabled = (key: string): boolean => { + // @ts-expect-error: if key isn't in globalOptions it'll be undefined which is false + return !!globalOptions[key]; +}; +``` + +## When Not To Use It + +If you are not using TypeScript 3.9 (or greater), then you will not be able to use this rule, as the directive is not supported + +## Further Reading + +- [Original Implementing PR](https://github.com/microsoft/TypeScript/pull/36014) diff --git a/packages/eslint-plugin/src/configs/all.json b/packages/eslint-plugin/src/configs/all.json index dc1ec0490ca..c736840a3ff 100644 --- a/packages/eslint-plugin/src/configs/all.json +++ b/packages/eslint-plugin/src/configs/all.json @@ -89,6 +89,7 @@ "@typescript-eslint/prefer-reduce-type-parameter": "error", "@typescript-eslint/prefer-regexp-exec": "error", "@typescript-eslint/prefer-string-starts-ends-with": "error", + "@typescript-eslint/prefer-ts-expect-error": "error", "@typescript-eslint/promise-function-async": "error", "quotes": "off", "@typescript-eslint/quotes": "error", diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index e941f5148c3..901d31a8b9f 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -78,6 +78,7 @@ import preferReadonlyParameterTypes from './prefer-readonly-parameter-types'; import preferReduceTypeParameter from './prefer-reduce-type-parameter'; import preferRegexpExec from './prefer-regexp-exec'; import preferStringStartsEndsWith from './prefer-string-starts-ends-with'; +import preferTsExpectError from './prefer-ts-expect-error'; import promiseFunctionAsync from './promise-function-async'; import quotes from './quotes'; import requireArraySortCompare from './require-array-sort-compare'; @@ -176,6 +177,7 @@ export default { 'prefer-reduce-type-parameter': preferReduceTypeParameter, 'prefer-regexp-exec': preferRegexpExec, 'prefer-string-starts-ends-with': preferStringStartsEndsWith, + 'prefer-ts-expect-error': preferTsExpectError, 'promise-function-async': promiseFunctionAsync, quotes: quotes, 'require-array-sort-compare': requireArraySortCompare, diff --git a/packages/eslint-plugin/src/rules/prefer-ts-expect-error.ts b/packages/eslint-plugin/src/rules/prefer-ts-expect-error.ts new file mode 100644 index 00000000000..1b87b132786 --- /dev/null +++ b/packages/eslint-plugin/src/rules/prefer-ts-expect-error.ts @@ -0,0 +1,55 @@ +import { AST_TOKEN_TYPES } from '@typescript-eslint/experimental-utils'; +import * as util from '../util'; + +type MessageIds = 'preferExpectErrorComment'; + +export default util.createRule<[], MessageIds>({ + name: 'prefer-ts-expect-error', + meta: { + type: 'problem', + docs: { + description: + 'Recommends using `// @ts-expect-error` over `// @ts-ignore`', + category: 'Best Practices', + recommended: false, + }, + fixable: 'code', + messages: { + preferExpectErrorComment: + 'Use "// @ts-expect-error" to ensure an error is actually being suppressed.', + }, + schema: [], + }, + defaultOptions: [], + create(context) { + const tsIgnoreRegExp = /^\/*\s*@ts-ignore/; + const sourceCode = context.getSourceCode(); + + return { + Program(): void { + const comments = sourceCode.getAllComments(); + + comments.forEach(comment => { + if (comment.type !== AST_TOKEN_TYPES.Line) { + return; + } + + if (tsIgnoreRegExp.test(comment.value)) { + context.report({ + node: comment, + messageId: 'preferExpectErrorComment', + fix: fixer => + fixer.replaceText( + comment, + `//${comment.value.replace( + '@ts-ignore', + '@ts-expect-error', + )}`, + ), + }); + } + }); + }, + }; + }, +}); diff --git a/packages/eslint-plugin/tests/rules/prefer-ts-expect-error.test.ts b/packages/eslint-plugin/tests/rules/prefer-ts-expect-error.test.ts new file mode 100644 index 00000000000..8e9c04f09ed --- /dev/null +++ b/packages/eslint-plugin/tests/rules/prefer-ts-expect-error.test.ts @@ -0,0 +1,85 @@ +import rule from '../../src/rules/prefer-ts-expect-error'; +import { RuleTester } from '../RuleTester'; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', +}); + +ruleTester.run('prefer-ts-expect-error', rule, { + valid: [ + '// @ts-nocheck', + '// @ts-check', + '// just a comment containing @ts-ignore somewhere', + '/* @ts-ignore */', + '/** @ts-ignore */', + ` +/* +// @ts-ignore in a block +*/ + `, + '// @ts-expect-error', + ` +if (false) { + // @ts-expect-error: Unreachable code error + console.log('hello'); +} + `, + ], + invalid: [ + { + code: '// @ts-ignore', + output: '// @ts-expect-error', + errors: [ + { + messageId: 'preferExpectErrorComment', + line: 1, + column: 1, + }, + ], + }, + { + code: '// @ts-ignore: Suppress next line', + output: '// @ts-expect-error: Suppress next line', + + errors: [ + { + messageId: 'preferExpectErrorComment', + line: 1, + column: 1, + }, + ], + }, + { + code: '/////@ts-ignore: Suppress next line', + output: '/////@ts-expect-error: Suppress next line', + errors: [ + { + messageId: 'preferExpectErrorComment', + line: 1, + column: 1, + }, + ], + }, + { + code: ` +if (false) { + // @ts-ignore: Unreachable code error + console.log('hello'); +} + `, + output: ` +if (false) { + // @ts-expect-error: Unreachable code error + console.log('hello'); +} + `, + errors: [ + { + messageId: 'preferExpectErrorComment', + line: 3, + column: 3, + }, + ], + }, + ], +}); diff --git a/packages/typescript-estree/src/ts-estree/ts-estree.ts b/packages/typescript-estree/src/ts-estree/ts-estree.ts index caf979a3012..5792ebc0205 100644 --- a/packages/typescript-estree/src/ts-estree/ts-estree.ts +++ b/packages/typescript-estree/src/ts-estree/ts-estree.ts @@ -115,6 +115,7 @@ export interface LineComment extends BaseToken { export type Comment = BlockComment | LineComment; export type Token = | BooleanToken + | Comment | IdentifierToken | JSXIdentifierToken | JSXTextToken From 795fd1c529ee58e97283c9ddf8463703517b50ab Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Sun, 12 Apr 2020 18:45:54 -0700 Subject: [PATCH 14/18] chore: add markdownlint (#1889) --- .github/workflows/ci.yml | 7 +- .markdownlint.json | 95 +++++++++++++ .markdownlintignore | 3 + .vscode/extensions.json | 3 +- .vscode/settings.json | 2 +- CONTRIBUTING.md | 3 +- docs/getting-started/README.md | 4 +- package.json | 3 + packages/eslint-plugin/ROADMAP.md | 20 +-- .../docs/rules/ban-ts-comment.md | 8 +- .../rules/class-literal-property-style.md | 6 +- .../docs/rules/no-base-to-string.md | 2 +- .../docs/rules/prefer-as-const.md | 2 +- packages/parser/README.md | 2 +- yarn.lock | 131 +++++++++++++++++- 15 files changed, 259 insertions(+), 32 deletions(-) create mode 100644 .markdownlint.json create mode 100644 .markdownlintignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f6904d879d..3e2d0e9dd51 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -93,10 +93,13 @@ jobs: - name: Check code formatting run: yarn format-check - - name: Run linting + - name: Lint code run: yarn lint - - name: Validate spelling + - name: Lint markdown + run: yarn lint:markdown + + - name: Check spelling run: yarn check:spelling integration_tests: diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 00000000000..77f01355394 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,95 @@ +{ + "default": false, + + // MD001/heading-increment/header-increment - Heading levels should only increment by one level at a time + "MD001": true, + // MD002/first-heading-h1/first-header-h1 - First heading should be a top level heading + "MD002": false, + // MD003/heading-style/header-style - Heading style + "MD003": false, + // MD004/ul-style - Unordered list style + "MD004": false, + // MD005/list-indent - Inconsistent indentation for list items at the same level + "MD005": false, + // MD006/ul-start-left - Consider starting bulleted lists at the beginning of the line + "MD006": false, + // MD007/ul-indent - Unordered list indentation + "MD007": false, + // MD009/no-trailing-spaces - Trailing spaces + "MD009": false, + // MD010/no-hard-tabs - Hard tabs + "MD010": false, + // MD011/no-reversed-links - Reversed link syntax + "MD011": true, + // MD012/no-multiple-blanks - Multiple consecutive blank lines + "MD012": false, + // MD013/line-length - Line length + "MD013": { "line_length": 99999 }, // no line length + // MD014/commands-show-output - Dollar signs used before commands without showing output + "MD014": false, + // MD018/no-missing-space-atx - No space after hash on atx style heading + "MD018": true, + // MD019/no-multiple-space-atx - Multiple spaces after hash on atx style heading + "MD019": false, + // MD020/no-missing-space-closed-atx - No space inside hashes on closed atx style heading + "MD020": false, + // MD021/no-multiple-space-closed-atx - Multiple spaces inside hashes on closed atx style heading + "MD021": false, + // MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines + "MD022": true, + // MD023/heading-start-left/header-start-left - Headings must start at the beginning of the line + "MD023": false, + // MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content + "MD024": false, + // MD025/single-title/single-h1 - Multiple top level headings in the same document + "MD025": true, + // MD026/no-trailing-punctuation - Trailing punctuation in heading + "MD026": { "punctuation": ".,;:!。,;:!?" }, // specifically allow "?" + // MD027/no-multiple-space-blockquote - Multiple spaces after blockquote symbol + "MD027": false, + // MD028/no-blanks-blockquote - Blank line inside blockquote + "MD028": true, + // MD029/ol-prefix - Ordered list item prefix + "MD029": false, + // MD030/list-marker-space - Spaces after list markers + "MD030": true, + // MD031/blanks-around-fences - Fenced code blocks should be surrounded by blank lines + "MD031": false, + // MD032/blanks-around-lists - Lists should be surrounded by blank lines + "MD032": false, + // MD033/no-inline-html - Inline HTML + "MD033": { "allowed_elements": ["a", "img", "br", "sup", "h1", "p"] }, + // MD034/no-bare-urls - Bare URL used + "MD034": false, + // MD035/hr-style - Horizontal rule style + "MD035": false, + // MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading + "MD036": true, + // MD037/no-space-in-emphasis - Spaces inside emphasis markers + "MD037": true, + // MD038/no-space-in-code - Spaces inside code span elements + "MD038": true, + // MD039/no-space-in-links - Spaces inside link text + "MD039": true, + // MD040/fenced-code-language - Fenced code blocks should have a language specified + "MD040": true, + // MD041/first-line-heading/first-line-h1 - First line in file should be a top level heading + "MD041": false, // would love to do this, but our README files use `

` as their heading + // MD042/no-empty-links - No empty links + "MD042": true, + // MD043/required-headings/required-headers - Required heading structure + "MD043": false, + // MD044/proper-names - Proper names should have the correct capitalization + "MD044": { + "names": ["JavaScript", "TypeScript", "TSLint", "ESLint"], + "code_blocks": false + }, + // MD045/no-alt-text - Images should have alternate text (alt text) + "MD045": true, + // MD046/code-block-style - Code block style + "MD046": { "style": "fenced" }, + // MD047/single-trailing-newline - Files should end with a single newline character + "MD047": false, + // MD048/code-fence-style - Code fence style + "MD048": { "style": "backtick" } +} diff --git a/.markdownlintignore b/.markdownlintignore new file mode 100644 index 00000000000..a5442e43554 --- /dev/null +++ b/.markdownlintignore @@ -0,0 +1,3 @@ +node_modules +CHANGELOG.md +tests/integration/fixtures/markdown diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 33430df8759..d91eb64d0d4 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,7 +3,8 @@ "esbenp.prettier-vscode", "dbaeumer.vscode-eslint", "editorconfig.editorconfig", - "streetsidesoftware.code-spell-checker" + "streetsidesoftware.code-spell-checker", + "davidanson.vscode-markdownlint" ], "unwantedRecommendations": ["hookyqr.beautify", "dbaeumer.jshint"] } diff --git a/.vscode/settings.json b/.vscode/settings.json index c03c3aac4dd..dc6744448f3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,5 +17,5 @@ "typescript.preferences.importModuleSpecifier": "auto", "javascript.preferences.quoteStyle": "single", "typescript.preferences.quoteStyle": "single", - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "esbenp.prettier-vscode", } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1df1de8f857..9cd07ba705b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,6 +50,7 @@ We have a sophisticated CI process setup which gets run on every PR. You must pa - Coverage reports should automatically be generated locally, and the `codecov` bot should also comment on your PR with the percentage, as well as links to the line-by-line coverage of each file touched by your PR. - Ensure you have no lint errors. - You can run `yarn lint` in any package or in the root. + - You can run `yarn lint:markdown` in the root. - If you have made changes to any markdown documentation, ensure there are no spelling errors - You can run `yarn check:spelling` in the root. - Or if you are using vscode, you can use [`Code Spell Checker`](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) plugin. @@ -60,7 +61,7 @@ We have a sophisticated CI process setup which gets run on every PR. You must pa Once your changes are ready, you can raise a PR. The title of your PR should match the following format: -``` +```text (): ``` diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index f3abf26f1c4..27a99d4b9fa 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -6,6 +6,6 @@ The goal of these docs are to give you a quick overview of the project and all o The docs are broken down into the following categories: -### [I want to lint my TypeScript codebase.](./linting/README.md) +## [I want to lint my TypeScript codebase.](./linting/README.md) -### [(TODO) I want to write an ESLint plugin in TypeScript.](./plugin-development/README.md) +## [(TODO) I want to write an ESLint plugin in TypeScript.](./plugin-development/README.md) diff --git a/package.json b/package.json index 5aa87589e19..562b5cd9370 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,8 @@ "kill-integration-test-containers": "docker-compose -f tests/integration/docker-compose.yml down -v --rmi local", "lint": "eslint . --ext .js,.ts", "lint-fix": "eslint . --ext .js,.ts --fix", + "lint:markdown": "markdownlint '**/*.md' --config=.markdownlint.json --ignore-path=.markdownlintignore", + "lint:markdown:fix": "lint:markdown --fix", "pre-commit": "yarn lint-staged", "pre-push": "yarn format-check", "postinstall": "lerna bootstrap && yarn build && lerna link", @@ -70,6 +72,7 @@ "jest": "^25.1.0", "lerna": "^3.20.2", "lint-staged": "^9.4.3", + "markdownlint-cli": "^0.22.0", "prettier": "^1.19.1", "ts-jest": "^25.0.0", "ts-node": "^8.5.0", diff --git a/packages/eslint-plugin/ROADMAP.md b/packages/eslint-plugin/ROADMAP.md index 93597358ab6..c6348ab2658 100644 --- a/packages/eslint-plugin/ROADMAP.md +++ b/packages/eslint-plugin/ROADMAP.md @@ -202,7 +202,7 @@ It lists all TSLint rules along side rules from the ESLint ecosystem that are th [1] Recommended config: `["error", { blankLine: "always", prev: "*", next: "return" }]`
[2] Doesn't check other control flow statements, such as `break` or `continue`. -## tslint-microsoft-contrib rules +## `tslint-microsoft-contrib` rules Rule listing is [here](https://github.com/Microsoft/tslint-microsoft-contrib#supported-rules). Deprecated rules are excluded (`missing-jsdoc`, `missing-optional-annotation`, `no-duplicate-case`, `no-duplicate-parameter-names`, `no-function-constructor-with-string-args`, `no-increment-decrement`, `no-empty-interfaces`, `no-missing-visibility-modifiers`, `no-multiple-var-decl`, `no-reserved-keywords`, `no-stateless-class`, `no-var-self`, `no-unnecessary-bind`, and `valid-typeof`). See the docs in the link above to find out what to use instead. @@ -211,7 +211,7 @@ Deprecated rules are excluded (`missing-jsdoc`, `missing-optional-annotation`, ` Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint-plugin-chai-expect-keywords), [`chai-expect`](https://github.com/Turbo87/eslint-plugin-chai-expect), [`chai-friendly`](https://github.com/ihordiachenko/eslint-plugin-chai-friendly), [`mocha`](https://github.com/lo1tuma/eslint-plugin-mocha), and [`jest`](https://github.com/jest-community/eslint-plugin-jest) -| tslint-microsoft-contrib rule | | ESLint rule | +| `tslint-microsoft-contrib` rule | | ESLint rule | | ---------------------------------- | :-: | ------------------------- | | `chai-prefer-contains-to-index-of` | 🛑 | N/A | | `chai-vague-errors` | 🛑 | N/A | @@ -220,16 +220,16 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint- ### TypeScript -| tslint-microsoft-contrib rule | | ESLint rule | -| ----------------------------- | :-: | ---------------------------------------------------------- | -| `prefer-array-literal` | 🌓 | [`@typescript-eslint/no-array-constructor`] [1] | -| `prefer-type-cast` | 🛑 | N/A | +| `tslint-microsoft-contrib` rule | | ESLint rule | +| ------------------------------- | :-: | ---------------------------------------------------------- | +| `prefer-array-literal` | 🌓 | [`@typescript-eslint/no-array-constructor`] [1] | +| `prefer-type-cast` | 🛑 | N/A | [1] ESLint rule is slightly less strict, allowing `new Array()` and `Array(2)`. ### Miscellaneous -| tslint-microsoft-contrib rule | | ESLint rule | +| `tslint-microsoft-contrib` rule | | ESLint rule | | ------------------------------------- | :-: | ---------------------------------------------------------------------- | | `export-name` | 🛑 | N/A ([relevant plugin][plugin:import]) | | `function-name` | 🛑 | N/A | @@ -274,7 +274,7 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint- ### Security -| tslint-microsoft-contrib rule | | ESLint rule | +| `tslint-microsoft-contrib` rule | | ESLint rule | | ------------------------------- | :-: | -------------------------------------------------- | | `no-disable-auto-sanitization` | 🛑 | N/A | | `no-document-domain` | 🌓 | Use [`no-restricted-syntax`][no-restricted-syntax] | @@ -291,7 +291,7 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint- ### Browser -| tslint-microsoft-contrib rule | | ESLint rule | +| `tslint-microsoft-contrib` rule | | ESLint rule | | ----------------------------------- | :-: | -------------------------------------------------- | | `jquery-deferred-must-complete` | 🛑 | N/A | | `no-backbone-get-set-outside-model` | 🛑 | N/A | @@ -306,7 +306,7 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint- ### React A11y -| tslint-microsoft-contrib rule | | ESLint rule | +| `tslint-microsoft-contrib` rule | | ESLint rule | | ----------------------------------------- | :-: | ---------------------------------------------------------- | | `react-a11y-accessible-headings` | 🌓 | [`jsx-a11y/heading-has-content`] [1] | | `react-a11y-anchors` | 🔌 | [`jsx-a11y/anchor-is-valid`] | diff --git a/packages/eslint-plugin/docs/rules/ban-ts-comment.md b/packages/eslint-plugin/docs/rules/ban-ts-comment.md index b1d93409c28..cb540bc8d09 100644 --- a/packages/eslint-plugin/docs/rules/ban-ts-comment.md +++ b/packages/eslint-plugin/docs/rules/ban-ts-comment.md @@ -5,7 +5,7 @@ Using these to suppress TypeScript Compiler Errors reduces the effectiveness of The directive comments supported by TypeScript are: -``` +```ts // @ts-expect-error // @ts-ignore // @ts-nocheck @@ -19,7 +19,7 @@ By default, only `@ts-check` is allowed, as it enables rather than suppresses er The configuration looks like this: -``` +```ts interface Options { 'ts-expect-error'?: boolean; 'ts-ignore'?: boolean; @@ -31,8 +31,8 @@ const defaultOptions: Options = { 'ts-expect-error': true, 'ts-ignore': true, 'ts-nocheck': true, - 'ts-check': false -} + 'ts-check': false, +}; ``` A value of `true` for a particular directive means that this rule will report if it finds any usage of said directive. diff --git a/packages/eslint-plugin/docs/rules/class-literal-property-style.md b/packages/eslint-plugin/docs/rules/class-literal-property-style.md index 903a695dd76..1bca8390fd3 100644 --- a/packages/eslint-plugin/docs/rules/class-literal-property-style.md +++ b/packages/eslint-plugin/docs/rules/class-literal-property-style.md @@ -1,7 +1,7 @@ # Ensures that literals on classes are exposed in a consistent style (`class-literal-property-style`) When writing TypeScript applications, it's typically safe to store literal values on classes using fields with the `readonly` modifier to prevent them from being reassigned. -When writing TypeScript libraries that could be used by Javascript users however, it's typically safer to expose these literals using `getter`s, since the `readonly` modifier is enforced at compile type. +When writing TypeScript libraries that could be used by JavaScript users however, it's typically safer to expose these literals using `getter`s, since the `readonly` modifier is enforced at compile type. ## Rule Details @@ -11,7 +11,7 @@ By default this rule prefers the `fields` style as it means JS doesn't have to s Note that this rule only checks for constant _literal_ values (string, template string, number, bigint, boolean, regexp, null). It does not check objects or arrays, because a readonly field behaves differently to a getter in those cases. It also does not check functions, as it is a common pattern to use readonly fields with arrow function values as auto-bound methods. This is because these types can be mutated and carry with them more complex implications about their usage. -#### The `fields` style +### The `fields` style This style checks for any getter methods that return literal values, and requires them to be defined using fields with the `readonly` modifier instead. @@ -50,7 +50,7 @@ class Mx { } ``` -#### The `getters` style +### The `getters` style This style checks for any `readonly` fields that are assigned literal values, and requires them to be defined as getters instead. This style pairs well with the [`@typescript-eslint/prefer-readonly`](prefer-readonly.md) rule, diff --git a/packages/eslint-plugin/docs/rules/no-base-to-string.md b/packages/eslint-plugin/docs/rules/no-base-to-string.md index 68bd82568b1..5c476b51303 100644 --- a/packages/eslint-plugin/docs/rules/no-base-to-string.md +++ b/packages/eslint-plugin/docs/rules/no-base-to-string.md @@ -1,6 +1,6 @@ # Requires that `.toString()` is only called on objects which provide useful information when stringified (`no-base-to-string`) -JavaScript will call `toString()` on an object when it is converted to a string, such as when `+` adding to a string or in `${}` template literals. +JavaScript will call `toString()` on an object when it is converted to a string, such as when `+` adding to a string or in `${}` template literals. The default Object `.toString()` returns `"[object Object]"`, so this rule requires stringified objects define a more useful `.toString()` method. diff --git a/packages/eslint-plugin/docs/rules/prefer-as-const.md b/packages/eslint-plugin/docs/rules/prefer-as-const.md index bb288192d26..b710a8275e7 100644 --- a/packages/eslint-plugin/docs/rules/prefer-as-const.md +++ b/packages/eslint-plugin/docs/rules/prefer-as-const.md @@ -25,4 +25,4 @@ let foo = { bar: 'baz' }; ## When Not To Use It -If you are using typescript < 3.4 +If you are using TypeScript < 3.4 diff --git a/packages/parser/README.md b/packages/parser/README.md index 70cb1cb2ca2..594f89dd6c2 100644 --- a/packages/parser/README.md +++ b/packages/parser/README.md @@ -67,7 +67,7 @@ Default `false`. Enable parsing JSX when `true`. More details can be found [here](https://www.typescriptlang.org/docs/handbook/jsx.html). -**NOTE:** this setting does not affect known file types (`.js`, `.jsx`, `.ts`, `.tsx`, `.json`) because the typescript compiler has its own internal handling for known file extensions. The exact behavior is as follows: +**NOTE:** this setting does not affect known file types (`.js`, `.jsx`, `.ts`, `.tsx`, `.json`) because the TypeScript compiler has its own internal handling for known file extensions. The exact behavior is as follows: - if `parserOptions.project` is _not_ provided: - `.js`, `.jsx`, `.tsx` files are parsed as if this is true. diff --git a/yarn.lock b/yarn.lock index 8fb6fd0a838..04ba7af56f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2340,6 +2340,13 @@ commander@^2.12.1, commander@^2.20.0, commander@^2.20.3, commander@~2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@~2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" + integrity sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q= + dependencies: + graceful-readlink ">= 1.0.0" + comment-json@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/comment-json/-/comment-json-1.1.3.tgz#6986c3330fee0c4c9e00c2398cd61afa5d8f239e" @@ -3006,6 +3013,16 @@ dedent@0.7.0, dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-extend@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.5.1.tgz#b894a9dd90d3023fbf1c55a394fb858eb2066f1f" + integrity sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w== + deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -3228,6 +3245,11 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: dependencies: once "^1.4.0" +entities@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" + integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== + env-paths@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" @@ -3939,6 +3961,11 @@ get-stdin@^4.0.1: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= +get-stdin@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" + integrity sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g= + get-stream@^4.0.0, get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -4045,7 +4072,7 @@ glob-to-regexp@^0.3.0: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -glob@*, glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@*, glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.2: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -4141,6 +4168,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== +"graceful-readlink@>= 1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= + growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" @@ -4342,7 +4374,7 @@ ignore@^4.0.3, ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.0.5, ignore@^5.1.1: +ignore@^5.0.5, ignore@^5.1.1, ignore@~5.1.4: version "5.1.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf" integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A== @@ -4426,7 +4458,7 @@ inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@^1.3.2, ini@^1.3.4: +ini@^1.3.2, ini@^1.3.4, ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== @@ -5223,7 +5255,7 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.13.1: +js-yaml@^3.13.1, js-yaml@~3.13.1: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== @@ -5321,6 +5353,11 @@ json5@2.x, json5@^2.1.0: dependencies: minimist "^1.2.0" +jsonc-parser@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.1.tgz#db73cd59d78cce28723199466b2a03d1be1df2bc" + integrity sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w== + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -5414,6 +5451,13 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= +linkify-it@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf" + integrity sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw== + dependencies: + uc.micro "^1.0.1" + lint-staged@^9.4.3: version "9.4.3" resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-9.4.3.tgz#f55ad5f94f6e105294bfd6499b23142961f7b982" @@ -5553,6 +5597,16 @@ lodash.clonedeep@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= +lodash.differencewith@~4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.differencewith/-/lodash.differencewith-4.5.0.tgz#bafafbc918b55154e179176a00bb0aefaac854b7" + integrity sha1-uvr7yRi1UVTheRdqALsK76rIVLc= + +lodash.flatten@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= + lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" @@ -5736,11 +5790,58 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +markdown-it@10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc" + integrity sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg== + dependencies: + argparse "^1.0.7" + entities "~2.0.0" + linkify-it "^2.0.0" + mdurl "^1.0.1" + uc.micro "^1.0.5" + +markdownlint-cli@^0.22.0: + version "0.22.0" + resolved "https://registry.yarnpkg.com/markdownlint-cli/-/markdownlint-cli-0.22.0.tgz#e5e3251ae6207a41eeb01640363fe2aa0f663a51" + integrity sha512-qRg6tK5dXWqkaFvEstz9YSQal1ECMgofrSZgdBOaPWG8cD50pk8Hs0ZpBCJ6SCHPKF71pCdtuSL2u82sIx2XWA== + dependencies: + commander "~2.9.0" + deep-extend "~0.5.1" + get-stdin "~5.0.1" + glob "~7.1.2" + ignore "~5.1.4" + js-yaml "~3.13.1" + jsonc-parser "~2.2.0" + lodash.differencewith "~4.5.0" + lodash.flatten "~4.4.0" + markdownlint "~0.19.0" + markdownlint-rule-helpers "~0.7.0" + minimatch "~3.0.4" + rc "~1.2.7" + +markdownlint-rule-helpers@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/markdownlint-rule-helpers/-/markdownlint-rule-helpers-0.7.0.tgz#66476c373fcad6727ac5b64cb662e900cbe46bfe" + integrity sha512-xZByWJNBaCMHo7nYPv/5aO8Jt68YcMvyouFXhuXmJzbqCsQy8rfCj0kYcv22kdK5PwAgMdbHg0hyTdURbUZtJw== + +markdownlint@~0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/markdownlint/-/markdownlint-0.19.0.tgz#a692c7b5c077874d4ee8b74e188d6c464ccce81e" + integrity sha512-+MsWOnYVUH4klcKM7iRx5cno9FQMDAb6FC6mWlZkeXPwIaK6Z5Vd9VkXkykPidRqmLHU2wI+MNyfUMnUCBw3pQ== + dependencies: + markdown-it "10.0.0" + marked@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/marked/-/marked-0.7.0.tgz#b64201f051d271b1edc10a04d1ae9b74bb8e5c0e" integrity sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg== +mdurl@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= + meow@5.0.0, meow@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/meow/-/meow-5.0.0.tgz#dfc73d63a9afc714a5e371760eb5c88b91078aa4" @@ -5851,7 +5952,7 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -minimatch@^3.0.4: +minimatch@^3.0.4, minimatch@~3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -6828,6 +6929,16 @@ quick-lru@^1.0.0: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g= +rc@~1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + react-is@^16.12.0: version "16.12.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" @@ -7783,6 +7894,11 @@ strip-json-comments@3.0.1, strip-json-comments@^3.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + strong-log-transformer@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" @@ -8169,6 +8285,11 @@ typescript@*, "typescript@>=3.2.1 <3.9.0", typescript@^3.8.3: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== +uc.micro@^1.0.1, uc.micro@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" + integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== + uglify-js@^3.1.4: version "3.6.0" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.0.tgz#704681345c53a8b2079fb6cec294b05ead242ff5" From f1c3b18f7aadc81f7dca7aa32aa1a8fe424e04e7 Mon Sep 17 00:00:00 2001 From: G r e y Date: Mon, 13 Apr 2020 11:28:56 -0500 Subject: [PATCH 15/18] fix(eslint-plugin): [unbound-method] false positive on property function initializer (#1890) --- packages/eslint-plugin/src/rules/unbound-method.ts | 5 +++++ .../tests/rules/unbound-method.test.ts | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/packages/eslint-plugin/src/rules/unbound-method.ts b/packages/eslint-plugin/src/rules/unbound-method.ts index 629aec50cca..fde58dc4e6f 100644 --- a/packages/eslint-plugin/src/rules/unbound-method.ts +++ b/packages/eslint-plugin/src/rules/unbound-method.ts @@ -198,6 +198,11 @@ function isDangerousMethod(symbol: ts.Symbol, ignoreStatic: boolean): boolean { } switch (valueDeclaration.kind) { + case ts.SyntaxKind.PropertyDeclaration: + return ( + (valueDeclaration as ts.PropertyDeclaration).initializer?.kind === + ts.SyntaxKind.FunctionExpression + ); case ts.SyntaxKind.MethodDeclaration: case ts.SyntaxKind.MethodSignature: return !( diff --git a/packages/eslint-plugin/tests/rules/unbound-method.test.ts b/packages/eslint-plugin/tests/rules/unbound-method.test.ts index 733b81e16b1..df1d50ac136 100644 --- a/packages/eslint-plugin/tests/rules/unbound-method.test.ts +++ b/packages/eslint-plugin/tests/rules/unbound-method.test.ts @@ -367,5 +367,19 @@ instance.unbound = x; // THIS SHOULD NOT }, ], }, + { + code: ` +class Foo { + unbound = function() {}; +} +const unbound = new Foo().unbound; + `, + errors: [ + { + line: 5, + messageId: 'unbound', + }, + ], + }, ], }); From 73675d1841ecbe9e8bf707478950d708592cbe06 Mon Sep 17 00:00:00 2001 From: James Henry Date: Mon, 13 Apr 2020 17:01:49 +0000 Subject: [PATCH 16/18] chore: publish v2.28.0 --- CHANGELOG.md | 29 ++++++++++++++++++++ lerna.json | 2 +- packages/eslint-plugin-internal/CHANGELOG.md | 8 ++++++ packages/eslint-plugin-internal/package.json | 4 +-- packages/eslint-plugin-tslint/CHANGELOG.md | 8 ++++++ packages/eslint-plugin-tslint/package.json | 6 ++-- packages/eslint-plugin/CHANGELOG.md | 29 ++++++++++++++++++++ packages/eslint-plugin/package.json | 4 +-- packages/experimental-utils/CHANGELOG.md | 11 ++++++++ packages/experimental-utils/package.json | 4 +-- packages/parser/CHANGELOG.md | 8 ++++++ packages/parser/package.json | 8 +++--- packages/shared-fixtures/CHANGELOG.md | 8 ++++++ packages/shared-fixtures/package.json | 2 +- packages/typescript-estree/CHANGELOG.md | 12 ++++++++ packages/typescript-estree/package.json | 4 +-- 16 files changed, 130 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17a398ca454..9d0a14f5669 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,35 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.28.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.27.0...v2.28.0) (2020-04-13) + + +### Bug Fixes + +* **eslint-plugin:** [method-signature-style] handle multiline params ([#1861](https://github.com/typescript-eslint/typescript-eslint/issues/1861)) ([5832a86](https://github.com/typescript-eslint/typescript-eslint/commit/5832a8643bbe174ec02df5966bb333e506e45f5d)) +* **eslint-plugin:** [no-empty-interface] use suggestion fixer for ambient contexts ([#1880](https://github.com/typescript-eslint/typescript-eslint/issues/1880)) ([62b2278](https://github.com/typescript-eslint/typescript-eslint/commit/62b2278aec0011c93eae17bed8b278114d3379a2)) +* **eslint-plugin:** [unbound-method] false positive on property function initializer ([#1890](https://github.com/typescript-eslint/typescript-eslint/issues/1890)) ([f1c3b18](https://github.com/typescript-eslint/typescript-eslint/commit/f1c3b18f7aadc81f7dca7aa32aa1a8fe424e04e7)) +* **eslint-plugin:** [unbound-method] ignore assignments _to_ methods ([#1736](https://github.com/typescript-eslint/typescript-eslint/issues/1736)) ([6b4680b](https://github.com/typescript-eslint/typescript-eslint/commit/6b4680b6e7343d9d98fa1de170f387a36d98b73e)) +* **eslint-plugin:** no-empty-interface autofix ([#1865](https://github.com/typescript-eslint/typescript-eslint/issues/1865)) ([829a2f7](https://github.com/typescript-eslint/typescript-eslint/commit/829a2f728f876d356908e2338c2d6620e58f9943)), closes [#1864](https://github.com/typescript-eslint/typescript-eslint/issues/1864) +* **eslint-plugin:** use `isTypeArrayTypeOrUnionOfArrayTypes` util for checking if type is array ([#1728](https://github.com/typescript-eslint/typescript-eslint/issues/1728)) ([05030f8](https://github.com/typescript-eslint/typescript-eslint/commit/05030f8d2bd5a50e95053bc61380891da71cc567)) + + +### Features + +* **eslint-plugin:** [ban-ts-comment] support `ts-expect-error` ([#1706](https://github.com/typescript-eslint/typescript-eslint/issues/1706)) ([469cff3](https://github.com/typescript-eslint/typescript-eslint/commit/469cff332c041f38f60de052769287342455cff1)) +* **eslint-plugin:** [consistent-type-assertions] always allow `const` assertions ([#1713](https://github.com/typescript-eslint/typescript-eslint/issues/1713)) ([af2c00d](https://github.com/typescript-eslint/typescript-eslint/commit/af2c00de62f7e31eaeb88996ebf3f330cc8473b9)) +* **eslint-plugin:** [explicit-function-return-type] add option to allow concise arrows that start with void ([#1732](https://github.com/typescript-eslint/typescript-eslint/issues/1732)) ([2e9c202](https://github.com/typescript-eslint/typescript-eslint/commit/2e9c2028a8a0b226e0f87d4bcc997fa259ca3ebd)) +* **eslint-plugin:** [explicit-module-boundary-types] add optio… ([#1778](https://github.com/typescript-eslint/typescript-eslint/issues/1778)) ([3eee804](https://github.com/typescript-eslint/typescript-eslint/commit/3eee804461d017ea6189cd7f64fcd473623684b4)) +* **eslint-plugin:** [no-base-to-string] add option to ignore tagged templates ([#1763](https://github.com/typescript-eslint/typescript-eslint/issues/1763)) ([f5edb99](https://github.com/typescript-eslint/typescript-eslint/commit/f5edb9938c33f8b68f026eba00db3abe9359ced3)) +* **eslint-plugin:** [restrict-template-expressions] add option `allowAny` ([#1762](https://github.com/typescript-eslint/typescript-eslint/issues/1762)) ([d44c0f9](https://github.com/typescript-eslint/typescript-eslint/commit/d44c0f9bed2404ca00b020b35fd825929e213398)) +* **eslint-plugin:** add rule `prefer-reduce-type-parameter` ([#1707](https://github.com/typescript-eslint/typescript-eslint/issues/1707)) ([c92d240](https://github.com/typescript-eslint/typescript-eslint/commit/c92d240e49113779053eac32038382b282812afc)) +* **eslint-plugin:** add rule `prefer-ts-expect-error` ([#1705](https://github.com/typescript-eslint/typescript-eslint/issues/1705)) ([7021f21](https://github.com/typescript-eslint/typescript-eslint/commit/7021f2151a25db2a8edf17e06cd6f21e90761ec8)) +* **eslint-plugin:** add rule no-unsafe-assignment ([#1694](https://github.com/typescript-eslint/typescript-eslint/issues/1694)) ([a49b860](https://github.com/typescript-eslint/typescript-eslint/commit/a49b860cbbb2c7d718b99f561e2fb6eaadf16f17)) + + + + + # [2.27.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.26.0...v2.27.0) (2020-04-06) diff --git a/lerna.json b/lerna.json index ecad9d3e932..dde84901cc1 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.27.0", + "version": "2.28.0", "npmClient": "yarn", "useWorkspaces": true, "stream": true diff --git a/packages/eslint-plugin-internal/CHANGELOG.md b/packages/eslint-plugin-internal/CHANGELOG.md index 00db774efba..c86faff06c0 100644 --- a/packages/eslint-plugin-internal/CHANGELOG.md +++ b/packages/eslint-plugin-internal/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.28.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.27.0...v2.28.0) (2020-04-13) + +**Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal + + + + + # [2.27.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.26.0...v2.27.0) (2020-04-06) diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json index c70d071c395..40e465ce4eb 100644 --- a/packages/eslint-plugin-internal/package.json +++ b/packages/eslint-plugin-internal/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin-internal", - "version": "2.27.0", + "version": "2.28.0", "private": true, "main": "dist/index.js", "scripts": { @@ -12,7 +12,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "2.27.0", + "@typescript-eslint/experimental-utils": "2.28.0", "prettier": "*" } } diff --git a/packages/eslint-plugin-tslint/CHANGELOG.md b/packages/eslint-plugin-tslint/CHANGELOG.md index feca294a7d3..3c242a42c63 100644 --- a/packages/eslint-plugin-tslint/CHANGELOG.md +++ b/packages/eslint-plugin-tslint/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.28.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.27.0...v2.28.0) (2020-04-13) + +**Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint + + + + + # [2.27.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.26.0...v2.27.0) (2020-04-06) **Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint diff --git a/packages/eslint-plugin-tslint/package.json b/packages/eslint-plugin-tslint/package.json index 5a3c9134f85..158d9a84b15 100644 --- a/packages/eslint-plugin-tslint/package.json +++ b/packages/eslint-plugin-tslint/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin-tslint", - "version": "2.27.0", + "version": "2.28.0", "main": "dist/index.js", "typings": "src/index.ts", "description": "TSLint wrapper plugin for ESLint", @@ -31,7 +31,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "2.27.0", + "@typescript-eslint/experimental-utils": "2.28.0", "lodash": "^4.17.15" }, "peerDependencies": { @@ -41,6 +41,6 @@ }, "devDependencies": { "@types/lodash": "^4.14.149", - "@typescript-eslint/parser": "2.27.0" + "@typescript-eslint/parser": "2.28.0" } } diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index 965a0e893be..72d30f22b36 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -3,6 +3,35 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.28.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.27.0...v2.28.0) (2020-04-13) + + +### Bug Fixes + +* **eslint-plugin:** [method-signature-style] handle multiline params ([#1861](https://github.com/typescript-eslint/typescript-eslint/issues/1861)) ([5832a86](https://github.com/typescript-eslint/typescript-eslint/commit/5832a8643bbe174ec02df5966bb333e506e45f5d)) +* **eslint-plugin:** [no-empty-interface] use suggestion fixer for ambient contexts ([#1880](https://github.com/typescript-eslint/typescript-eslint/issues/1880)) ([62b2278](https://github.com/typescript-eslint/typescript-eslint/commit/62b2278aec0011c93eae17bed8b278114d3379a2)) +* **eslint-plugin:** [unbound-method] false positive on property function initializer ([#1890](https://github.com/typescript-eslint/typescript-eslint/issues/1890)) ([f1c3b18](https://github.com/typescript-eslint/typescript-eslint/commit/f1c3b18f7aadc81f7dca7aa32aa1a8fe424e04e7)) +* **eslint-plugin:** [unbound-method] ignore assignments _to_ methods ([#1736](https://github.com/typescript-eslint/typescript-eslint/issues/1736)) ([6b4680b](https://github.com/typescript-eslint/typescript-eslint/commit/6b4680b6e7343d9d98fa1de170f387a36d98b73e)) +* **eslint-plugin:** no-empty-interface autofix ([#1865](https://github.com/typescript-eslint/typescript-eslint/issues/1865)) ([829a2f7](https://github.com/typescript-eslint/typescript-eslint/commit/829a2f728f876d356908e2338c2d6620e58f9943)), closes [#1864](https://github.com/typescript-eslint/typescript-eslint/issues/1864) +* **eslint-plugin:** use `isTypeArrayTypeOrUnionOfArrayTypes` util for checking if type is array ([#1728](https://github.com/typescript-eslint/typescript-eslint/issues/1728)) ([05030f8](https://github.com/typescript-eslint/typescript-eslint/commit/05030f8d2bd5a50e95053bc61380891da71cc567)) + + +### Features + +* **eslint-plugin:** [ban-ts-comment] support `ts-expect-error` ([#1706](https://github.com/typescript-eslint/typescript-eslint/issues/1706)) ([469cff3](https://github.com/typescript-eslint/typescript-eslint/commit/469cff332c041f38f60de052769287342455cff1)) +* **eslint-plugin:** [consistent-type-assertions] always allow `const` assertions ([#1713](https://github.com/typescript-eslint/typescript-eslint/issues/1713)) ([af2c00d](https://github.com/typescript-eslint/typescript-eslint/commit/af2c00de62f7e31eaeb88996ebf3f330cc8473b9)) +* **eslint-plugin:** [explicit-function-return-type] add option to allow concise arrows that start with void ([#1732](https://github.com/typescript-eslint/typescript-eslint/issues/1732)) ([2e9c202](https://github.com/typescript-eslint/typescript-eslint/commit/2e9c2028a8a0b226e0f87d4bcc997fa259ca3ebd)) +* **eslint-plugin:** [explicit-module-boundary-types] add optio… ([#1778](https://github.com/typescript-eslint/typescript-eslint/issues/1778)) ([3eee804](https://github.com/typescript-eslint/typescript-eslint/commit/3eee804461d017ea6189cd7f64fcd473623684b4)) +* **eslint-plugin:** [no-base-to-string] add option to ignore tagged templates ([#1763](https://github.com/typescript-eslint/typescript-eslint/issues/1763)) ([f5edb99](https://github.com/typescript-eslint/typescript-eslint/commit/f5edb9938c33f8b68f026eba00db3abe9359ced3)) +* **eslint-plugin:** [restrict-template-expressions] add option `allowAny` ([#1762](https://github.com/typescript-eslint/typescript-eslint/issues/1762)) ([d44c0f9](https://github.com/typescript-eslint/typescript-eslint/commit/d44c0f9bed2404ca00b020b35fd825929e213398)) +* **eslint-plugin:** add rule `prefer-reduce-type-parameter` ([#1707](https://github.com/typescript-eslint/typescript-eslint/issues/1707)) ([c92d240](https://github.com/typescript-eslint/typescript-eslint/commit/c92d240e49113779053eac32038382b282812afc)) +* **eslint-plugin:** add rule `prefer-ts-expect-error` ([#1705](https://github.com/typescript-eslint/typescript-eslint/issues/1705)) ([7021f21](https://github.com/typescript-eslint/typescript-eslint/commit/7021f2151a25db2a8edf17e06cd6f21e90761ec8)) +* **eslint-plugin:** add rule no-unsafe-assignment ([#1694](https://github.com/typescript-eslint/typescript-eslint/issues/1694)) ([a49b860](https://github.com/typescript-eslint/typescript-eslint/commit/a49b860cbbb2c7d718b99f561e2fb6eaadf16f17)) + + + + + # [2.27.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.26.0...v2.27.0) (2020-04-06) diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 290f200a0d3..d872079514a 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin", - "version": "2.27.0", + "version": "2.28.0", "description": "TypeScript plugin for ESLint", "keywords": [ "eslint", @@ -41,7 +41,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "2.27.0", + "@typescript-eslint/experimental-utils": "2.28.0", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.0.0", "tsutils": "^3.17.1" diff --git a/packages/experimental-utils/CHANGELOG.md b/packages/experimental-utils/CHANGELOG.md index b874b0c440a..ece5fcbb544 100644 --- a/packages/experimental-utils/CHANGELOG.md +++ b/packages/experimental-utils/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.28.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.27.0...v2.28.0) (2020-04-13) + + +### Features + +* **eslint-plugin:** add rule `prefer-reduce-type-parameter` ([#1707](https://github.com/typescript-eslint/typescript-eslint/issues/1707)) ([c92d240](https://github.com/typescript-eslint/typescript-eslint/commit/c92d240e49113779053eac32038382b282812afc)) + + + + + # [2.27.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.26.0...v2.27.0) (2020-04-06) diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json index d1550271300..b623c12ed31 100644 --- a/packages/experimental-utils/package.json +++ b/packages/experimental-utils/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/experimental-utils", - "version": "2.27.0", + "version": "2.28.0", "description": "(Experimental) Utilities for working with TypeScript + ESLint together", "keywords": [ "eslint", @@ -37,7 +37,7 @@ }, "dependencies": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "2.27.0", + "@typescript-eslint/typescript-estree": "2.28.0", "eslint-scope": "^5.0.0", "eslint-utils": "^2.0.0" }, diff --git a/packages/parser/CHANGELOG.md b/packages/parser/CHANGELOG.md index c13d0782a4b..c99a618a07e 100644 --- a/packages/parser/CHANGELOG.md +++ b/packages/parser/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.28.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.27.0...v2.28.0) (2020-04-13) + +**Note:** Version bump only for package @typescript-eslint/parser + + + + + # [2.27.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.26.0...v2.27.0) (2020-04-06) **Note:** Version bump only for package @typescript-eslint/parser diff --git a/packages/parser/package.json b/packages/parser/package.json index d59732165ac..a36625794ad 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/parser", - "version": "2.27.0", + "version": "2.28.0", "description": "An ESLint custom parser which leverages TypeScript ESTree", "main": "dist/parser.js", "types": "dist/parser.d.ts", @@ -43,13 +43,13 @@ }, "dependencies": { "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "2.27.0", - "@typescript-eslint/typescript-estree": "2.27.0", + "@typescript-eslint/experimental-utils": "2.28.0", + "@typescript-eslint/typescript-estree": "2.28.0", "eslint-visitor-keys": "^1.1.0" }, "devDependencies": { "@types/glob": "^7.1.1", - "@typescript-eslint/shared-fixtures": "2.27.0", + "@typescript-eslint/shared-fixtures": "2.28.0", "glob": "*" }, "peerDependenciesMeta": { diff --git a/packages/shared-fixtures/CHANGELOG.md b/packages/shared-fixtures/CHANGELOG.md index 8ac0c5e5368..f460b702eb9 100644 --- a/packages/shared-fixtures/CHANGELOG.md +++ b/packages/shared-fixtures/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.28.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.27.0...v2.28.0) (2020-04-13) + +**Note:** Version bump only for package @typescript-eslint/shared-fixtures + + + + + # [2.27.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.26.0...v2.27.0) (2020-04-06) **Note:** Version bump only for package @typescript-eslint/shared-fixtures diff --git a/packages/shared-fixtures/package.json b/packages/shared-fixtures/package.json index c36d658dad5..e843de63a56 100644 --- a/packages/shared-fixtures/package.json +++ b/packages/shared-fixtures/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/shared-fixtures", - "version": "2.27.0", + "version": "2.28.0", "private": true, "scripts": { "build": "tsc -b tsconfig.build.json", diff --git a/packages/typescript-estree/CHANGELOG.md b/packages/typescript-estree/CHANGELOG.md index 0d46bcbc15d..8750d8a65ed 100644 --- a/packages/typescript-estree/CHANGELOG.md +++ b/packages/typescript-estree/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.28.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.27.0...v2.28.0) (2020-04-13) + + +### Features + +* **eslint-plugin:** add rule `prefer-ts-expect-error` ([#1705](https://github.com/typescript-eslint/typescript-eslint/issues/1705)) ([7021f21](https://github.com/typescript-eslint/typescript-eslint/commit/7021f2151a25db2a8edf17e06cd6f21e90761ec8)) +* **eslint-plugin:** add rule no-unsafe-assignment ([#1694](https://github.com/typescript-eslint/typescript-eslint/issues/1694)) ([a49b860](https://github.com/typescript-eslint/typescript-eslint/commit/a49b860cbbb2c7d718b99f561e2fb6eaadf16f17)) + + + + + # [2.27.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.26.0...v2.27.0) (2020-04-06) diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index 7667f340314..bc75ebdf786 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/typescript-estree", - "version": "2.27.0", + "version": "2.28.0", "description": "A parser that converts TypeScript source code into an ESTree compatible form", "main": "dist/parser.js", "types": "dist/parser.d.ts", @@ -58,7 +58,7 @@ "@types/lodash": "^4.14.149", "@types/semver": "^6.2.0", "@types/tmp": "^0.1.0", - "@typescript-eslint/shared-fixtures": "2.27.0", + "@typescript-eslint/shared-fixtures": "2.28.0", "tmp": "^0.1.0", "typescript": "*" }, From cc70e4fbadd0b15fd6af913a2e1e2ddd346fa558 Mon Sep 17 00:00:00 2001 From: ulrichb Date: Tue, 14 Apr 2020 18:15:08 +0200 Subject: [PATCH 17/18] feat(eslint-plugin): [restrict-template-expressions] add support for intersection types (#1803) --- .../rules/restrict-template-expressions.md | 5 + .../rules/restrict-template-expressions.ts | 142 ++++++++---------- .../restrict-template-expressions.test.ts | 21 +++ 3 files changed, 90 insertions(+), 78 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/restrict-template-expressions.md b/packages/eslint-plugin/docs/rules/restrict-template-expressions.md index 4c52e74445f..816a0b0f9d6 100644 --- a/packages/eslint-plugin/docs/rules/restrict-template-expressions.md +++ b/packages/eslint-plugin/docs/rules/restrict-template-expressions.md @@ -6,6 +6,9 @@ Examples of **correct** code: const arg = 'foo'; const msg1 = `arg = ${arg}`; const msg2 = `arg = ${arg || 'default'}`; + +const stringWithKindProp: string & { _kind?: 'MyString' } = 'foo'; +const msg3 = `stringWithKindProp = ${stringWithKindProp}`; ``` Examples of **incorrect** code: @@ -28,6 +31,8 @@ type Options = { allowNumber?: boolean; // if true, also allow boolean type in template expressions allowBoolean?: boolean; + // if true, also allow any in template expressions + allowAny?: boolean; // if true, also allow null and undefined in template expressions allowNullable?: boolean; }; diff --git a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts index d73d8d98a25..90dd363f820 100644 --- a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts +++ b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts @@ -7,10 +7,10 @@ import * as util from '../util'; type Options = [ { - allowNullable?: boolean; allowNumber?: boolean; allowBoolean?: boolean; allowAny?: boolean; + allowNullable?: boolean; }, ]; @@ -33,10 +33,10 @@ export default util.createRule({ { type: 'object', properties: { - allowAny: { type: 'boolean' }, + allowNumber: { type: 'boolean' }, allowBoolean: { type: 'boolean' }, + allowAny: { type: 'boolean' }, allowNullable: { type: 'boolean' }, - allowNumber: { type: 'boolean' }, }, }, ], @@ -46,31 +46,40 @@ export default util.createRule({ const service = util.getParserServices(context); const typeChecker = service.program.getTypeChecker(); - type BaseType = - | 'string' - | 'number' - | 'bigint' - | 'boolean' - | 'null' - | 'undefined' - | 'any' - | 'other'; - - const allowedTypes: BaseType[] = [ - 'string', - ...(options.allowNumber ? (['number', 'bigint'] as const) : []), - ...(options.allowBoolean ? (['boolean'] as const) : []), - ...(options.allowNullable ? (['null', 'undefined'] as const) : []), - ...(options.allowAny ? (['any'] as const) : []), - ]; - - function isAllowedType(types: BaseType[]): boolean { - for (const type of types) { - if (!allowedTypes.includes(type)) { - return false; - } + function isUnderlyingTypePrimitive(type: ts.Type): boolean { + if (util.isTypeFlagSet(type, ts.TypeFlags.StringLike)) { + return true; + } + + if ( + options.allowNumber && + util.isTypeFlagSet( + type, + ts.TypeFlags.NumberLike | ts.TypeFlags.BigIntLike, + ) + ) { + return true; } - return true; + + if ( + options.allowBoolean && + util.isTypeFlagSet(type, ts.TypeFlags.BooleanLike) + ) { + return true; + } + + if (options.allowAny && util.isTypeFlagSet(type, ts.TypeFlags.Any)) { + return true; + } + + if ( + options.allowNullable && + util.isTypeFlagSet(type, ts.TypeFlags.Null | ts.TypeFlags.Undefined) + ) { + return true; + } + + return false; } return { @@ -80,11 +89,15 @@ export default util.createRule({ return; } - for (const expr of node.expressions) { - const type = getNodeType(expr); - if (!isAllowedType(type)) { + for (const expression of node.expressions) { + if ( + !isUnderlyingExpressionTypeConfirmingTo( + expression, + isUnderlyingTypePrimitive, + ) + ) { context.report({ - node: expr, + node: expression, messageId: 'invalidType', }); } @@ -92,58 +105,31 @@ export default util.createRule({ }, }; - /** - * Helper function to get base type of node - * @param node the node to be evaluated. - */ - function getNodeType(node: TSESTree.Expression): BaseType[] { - const tsNode = service.esTreeNodeToTSNodeMap.get(node); - const type = util.getConstrainedTypeAtLocation(typeChecker, tsNode); + function isUnderlyingExpressionTypeConfirmingTo( + expression: TSESTree.Expression, + predicate: (underlyingType: ts.Type) => boolean, + ): boolean { + return rec(getExpressionNodeType(expression)); - return getBaseType(type); - } - - function getBaseType(type: ts.Type): BaseType[] { - if (type.isStringLiteral()) { - return ['string']; - } - if (type.isNumberLiteral()) { - return ['number']; - } - if (type.flags & ts.TypeFlags.BigIntLiteral) { - return ['bigint']; - } - if (type.flags & ts.TypeFlags.BooleanLiteral) { - return ['boolean']; - } - if (type.flags & ts.TypeFlags.Null) { - return ['null']; - } - if (type.flags & ts.TypeFlags.Undefined) { - return ['undefined']; - } - if (type.flags & ts.TypeFlags.Any) { - return ['any']; - } + function rec(type: ts.Type): boolean { + if (type.isUnion()) { + return type.types.every(rec); + } - if (type.isUnion()) { - return type.types - .map(getBaseType) - .reduce((all, array) => [...all, ...array], []); - } + if (type.isIntersection()) { + return type.types.some(rec); + } - const stringType = typeChecker.typeToString(type); - if ( - stringType === 'string' || - stringType === 'number' || - stringType === 'bigint' || - stringType === 'boolean' || - stringType === 'any' - ) { - return [stringType]; + return predicate(type); } + } - return ['other']; + /** + * Helper function to extract the TS type of an TSESTree expression. + */ + function getExpressionNodeType(node: TSESTree.Expression): ts.Type { + const tsNode = service.esTreeNodeToTSNodeMap.get(node); + return util.getConstrainedTypeAtLocation(typeChecker, tsNode); } }, }); diff --git a/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts b/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts index 6a2861efa32..2f5900f3d2a 100644 --- a/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts +++ b/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts @@ -30,6 +30,12 @@ ruleTester.run('restrict-template-expressions', rule, { return \`arg = \${arg}\`; } `, + // Base case - intersection type + ` + function test(arg: T) { + return \`arg = \${arg}\`; + } + `, // Base case - don't check tagged templates ` tag\`arg = \${null}\`; @@ -68,6 +74,14 @@ ruleTester.run('restrict-template-expressions', rule, { } `, }, + { + options: [{ allowNumber: true }], + code: ` + function test(arg: T) { + return \`arg = \${arg}\`; + } + `, + }, { options: [{ allowNumber: true }], code: ` @@ -236,6 +250,13 @@ ruleTester.run('restrict-template-expressions', rule, { `, errors: [{ messageId: 'invalidType', line: 3, column: 30 }], }, + { + code: ` + declare const arg: { a: string } & { b: string }; + const msg = \`arg = \${arg}\`; + `, + errors: [{ messageId: 'invalidType', line: 3, column: 30 }], + }, { options: [{ allowNumber: true, allowBoolean: true, allowNullable: true }], code: ` From 369978e9685bacb3e3882b0510ff06eaf8df4ca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20Unneb=C3=A4ck?= Date: Mon, 20 Apr 2020 02:42:03 +0100 Subject: [PATCH 18/18] fix(eslint-plugin): [no-base-to-string] soft remove `ignoreTaggedTemplateExpressions` option (#1916) --- .../docs/rules/no-base-to-string.md | 26 ------------------- .../src/rules/no-base-to-string.ts | 8 +++--- .../tests/rules/no-base-to-string.test.ts | 19 +++----------- 3 files changed, 8 insertions(+), 45 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/no-base-to-string.md b/packages/eslint-plugin/docs/rules/no-base-to-string.md index 5c476b51303..569ffe0e6d9 100644 --- a/packages/eslint-plugin/docs/rules/no-base-to-string.md +++ b/packages/eslint-plugin/docs/rules/no-base-to-string.md @@ -52,32 +52,6 @@ const literalWithToString = { `Value: ${literalWithToString}`; ``` -## Options - -The rule accepts an options object with the following properties: - -```ts -type Options = { - // if true, interpolated expressions in tagged templates will not be checked - ignoreTaggedTemplateExpressions?: boolean; -}; - -const defaults = { - ignoreTaggedTemplateExpressions: false, -}; -``` - -### `ignoreTaggedTemplateExpressions` - -This allows to skip checking tagged templates, for cases where the tags do not necessarily stringify interpolated values. - -Examples of additional **correct** code for this rule with `{ ignoreTaggedTemplateExpressions: true }`: - -```ts -function tag() {} -tag`${{}}`; -``` - ## When Not To Use It If you don't mind `"[object Object]"` in your strings, then you will not need this rule. diff --git a/packages/eslint-plugin/src/rules/no-base-to-string.ts b/packages/eslint-plugin/src/rules/no-base-to-string.ts index 47803f8e2aa..a1121f37abd 100644 --- a/packages/eslint-plugin/src/rules/no-base-to-string.ts +++ b/packages/eslint-plugin/src/rules/no-base-to-string.ts @@ -14,6 +14,7 @@ enum Usefulness { type Options = [ { + /** @deprecated This option is now ignored and treated as always true, it will be removed in 3.0 */ ignoreTaggedTemplateExpressions?: boolean; }, ]; @@ -39,7 +40,7 @@ export default util.createRule({ properties: { ignoreTaggedTemplateExpressions: { type: 'boolean', - default: false, + default: true, }, }, additionalProperties: false, @@ -47,8 +48,8 @@ export default util.createRule({ ], type: 'suggestion', }, - defaultOptions: [{ ignoreTaggedTemplateExpressions: false }], - create(context, [options]) { + defaultOptions: [{ ignoreTaggedTemplateExpressions: true }], + create(context) { const parserServices = util.getParserServices(context); const typeChecker = parserServices.program.getTypeChecker(); @@ -130,7 +131,6 @@ export default util.createRule({ }, TemplateLiteral(node: TSESTree.TemplateLiteral): void { if ( - options.ignoreTaggedTemplateExpressions && node.parent && node.parent.type === AST_NODE_TYPES.TaggedTemplateExpression ) { diff --git a/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts b/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts index 59abfbb1f60..82bd4e0302e 100644 --- a/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts +++ b/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts @@ -70,6 +70,10 @@ const literalWithToString = { 'let _ = {} ^ {};', 'let _ = {} << {};', 'let _ = {} >> {};', + ` +function tag() {} +tag\`\${{}}\`; + `, { code: ` function tag() {} @@ -95,21 +99,6 @@ const literalWithToString = { }, ], }, - { - code: ` - function tag() {} - tag\`\${{}}\`; - `, - errors: [ - { - data: { - certainty: 'will', - name: '{}', - }, - messageId: 'baseToString', - }, - ], - }, { code: '({}.toString());', errors: [