diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml
new file mode 100644
index 000000000000..bde4f5f8bd53
--- /dev/null
+++ b/.github/workflows/lock.yml
@@ -0,0 +1,20 @@
+name: "Lock threads"
+
+on:
+ schedule:
+ # TODO - change to "0 0 * * *" once the backlog is processed
+ - cron: "0 * * * *"
+
+jobs:
+ lock:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: dessant/lock-threads@v2
+ with:
+ github-token: ${{ github.token }}
+ issue-lock-inactive-days: "30"
+ issue-lock-reason: "resolved"
+ issue-lock-comment: ""
+ pr-lock-inactive-days: "30"
+ pr-lock-reason: "resolved"
+ pr-lock-comment: ""
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9d0a14f5669d..0921927763f4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,25 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [2.29.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.28.0...v2.29.0) (2020-04-20)
+
+
+### Bug Fixes
+
+* **eslint-plugin:** [no-base-to-string] soft remove `ignoreTaggedTemplateExpressions` option ([#1916](https://github.com/typescript-eslint/typescript-eslint/issues/1916)) ([369978e](https://github.com/typescript-eslint/typescript-eslint/commit/369978e9685bacb3e3882b0510ff06eaf8df4ca1))
+
+
+### Features
+
+* **eslint-plugin:** [no-floating-promise] add option to ignore IIFEs ([#1799](https://github.com/typescript-eslint/typescript-eslint/issues/1799)) ([cea51bf](https://github.com/typescript-eslint/typescript-eslint/commit/cea51bf130d6d3c2935f5e2dcc468196f2ad9d00))
+* **eslint-plugin:** [restrict-template-expressions] add support for intersection types ([#1803](https://github.com/typescript-eslint/typescript-eslint/issues/1803)) ([cc70e4f](https://github.com/typescript-eslint/typescript-eslint/commit/cc70e4fbadd0b15fd6af913a2e1e2ddd346fa558))
+* **eslint-plugin:** add extension rule `init-declarations` ([#1814](https://github.com/typescript-eslint/typescript-eslint/issues/1814)) ([b01f5e7](https://github.com/typescript-eslint/typescript-eslint/commit/b01f5e778ac28e0797a3734fc58d025bb224f418))
+* **eslint-plugin:** add extension rule `keyword-spacing` ([#1739](https://github.com/typescript-eslint/typescript-eslint/issues/1739)) ([c5106dd](https://github.com/typescript-eslint/typescript-eslint/commit/c5106dd4bf2bc8846cc39aa8bb50c33bec026d4d))
+
+
+
+
+
# [2.28.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.27.0...v2.28.0) (2020-04-13)
diff --git a/lerna.json b/lerna.json
index dde84901cc10..3c1dacefef4d 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "2.28.0",
+ "version": "2.29.0",
"npmClient": "yarn",
"useWorkspaces": true,
"stream": true
diff --git a/packages/eslint-plugin-internal/CHANGELOG.md b/packages/eslint-plugin-internal/CHANGELOG.md
index c86faff06c0c..1634ffba2fa8 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.29.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.28.0...v2.29.0) (2020-04-20)
+
+**Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal
+
+
+
+
+
# [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
diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json
index 40e465ce4eb4..6348c281ab4e 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.28.0",
+ "version": "2.29.0",
"private": true,
"main": "dist/index.js",
"scripts": {
@@ -12,7 +12,7 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/experimental-utils": "2.28.0",
+ "@typescript-eslint/experimental-utils": "2.29.0",
"prettier": "*"
}
}
diff --git a/packages/eslint-plugin-tslint/CHANGELOG.md b/packages/eslint-plugin-tslint/CHANGELOG.md
index 3c242a42c633..d43881609278 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.29.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.28.0...v2.29.0) (2020-04-20)
+
+**Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint
+
+
+
+
+
# [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
diff --git a/packages/eslint-plugin-tslint/package.json b/packages/eslint-plugin-tslint/package.json
index 158d9a84b159..fa6905b5a4f0 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.28.0",
+ "version": "2.29.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.28.0",
+ "@typescript-eslint/experimental-utils": "2.29.0",
"lodash": "^4.17.15"
},
"peerDependencies": {
@@ -41,6 +41,6 @@
},
"devDependencies": {
"@types/lodash": "^4.14.149",
- "@typescript-eslint/parser": "2.28.0"
+ "@typescript-eslint/parser": "2.29.0"
}
}
diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md
index 72d30f22b36c..37883be34309 100644
--- a/packages/eslint-plugin/CHANGELOG.md
+++ b/packages/eslint-plugin/CHANGELOG.md
@@ -3,6 +3,25 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [2.29.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.28.0...v2.29.0) (2020-04-20)
+
+
+### Bug Fixes
+
+* **eslint-plugin:** [no-base-to-string] soft remove `ignoreTaggedTemplateExpressions` option ([#1916](https://github.com/typescript-eslint/typescript-eslint/issues/1916)) ([369978e](https://github.com/typescript-eslint/typescript-eslint/commit/369978e9685bacb3e3882b0510ff06eaf8df4ca1))
+
+
+### Features
+
+* **eslint-plugin:** [no-floating-promise] add option to ignore IIFEs ([#1799](https://github.com/typescript-eslint/typescript-eslint/issues/1799)) ([cea51bf](https://github.com/typescript-eslint/typescript-eslint/commit/cea51bf130d6d3c2935f5e2dcc468196f2ad9d00))
+* **eslint-plugin:** [restrict-template-expressions] add support for intersection types ([#1803](https://github.com/typescript-eslint/typescript-eslint/issues/1803)) ([cc70e4f](https://github.com/typescript-eslint/typescript-eslint/commit/cc70e4fbadd0b15fd6af913a2e1e2ddd346fa558))
+* **eslint-plugin:** add extension rule `init-declarations` ([#1814](https://github.com/typescript-eslint/typescript-eslint/issues/1814)) ([b01f5e7](https://github.com/typescript-eslint/typescript-eslint/commit/b01f5e778ac28e0797a3734fc58d025bb224f418))
+* **eslint-plugin:** add extension rule `keyword-spacing` ([#1739](https://github.com/typescript-eslint/typescript-eslint/issues/1739)) ([c5106dd](https://github.com/typescript-eslint/typescript-eslint/commit/c5106dd4bf2bc8846cc39aa8bb50c33bec026d4d))
+
+
+
+
+
# [2.28.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.27.0...v2.28.0) (2020-04-13)
diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md
index 7ed22981926c..c4507267c6f0 100644
--- a/packages/eslint-plugin/README.md
+++ b/packages/eslint-plugin/README.md
@@ -183,6 +183,8 @@ In these cases, we create what we call an extension rule; a rule within our plug
| [`@typescript-eslint/default-param-last`](./docs/rules/default-param-last.md) | Enforce default parameters to be last | | | |
| [`@typescript-eslint/func-call-spacing`](./docs/rules/func-call-spacing.md) | Require or disallow spacing between function identifiers and their invocations | | :wrench: | |
| [`@typescript-eslint/indent`](./docs/rules/indent.md) | Enforce consistent indentation | | :wrench: | |
+| [`@typescript-eslint/init-declarations`](./docs/rules/init-declarations.md) | require or disallow initialization in variable declarations | | | |
+| [`@typescript-eslint/keyword-spacing`](./docs/rules/keyword-spacing.md) | Enforce consistent spacing before and after keywords | | :wrench: | |
| [`@typescript-eslint/no-array-constructor`](./docs/rules/no-array-constructor.md) | Disallow generic `Array` constructors | :heavy_check_mark: | :wrench: | |
| [`@typescript-eslint/no-dupe-class-members`](./docs/rules/no-dupe-class-members.md) | Disallow duplicate class members | | | |
| [`@typescript-eslint/no-empty-function`](./docs/rules/no-empty-function.md) | Disallow empty functions | :heavy_check_mark: | | |
diff --git a/packages/eslint-plugin/docs/rules/init-declarations.md b/packages/eslint-plugin/docs/rules/init-declarations.md
new file mode 100644
index 000000000000..8888e2efef27
--- /dev/null
+++ b/packages/eslint-plugin/docs/rules/init-declarations.md
@@ -0,0 +1,22 @@
+# require or disallow initialization in variable declarations (`init-declarations`)
+
+## Rule Details
+
+This rule extends the base [`eslint/init-declarations`](https://eslint.org/docs/rules/init-declarations) rule.
+It adds support for TypeScript's `declare` variables.
+
+## How to use
+
+```cjson
+{
+ // note you must disable the base rule as it can report incorrect errors
+ "init-declarations": "off",
+ "@typescript-eslint/init-declarations": ["error"]
+}
+```
+
+## Options
+
+See [`eslint/init-declarations` options](https://eslint.org/docs/rules/init-declarations#options).
+
+Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/init-declarations.md)
diff --git a/packages/eslint-plugin/docs/rules/keyword-spacing.md b/packages/eslint-plugin/docs/rules/keyword-spacing.md
new file mode 100644
index 000000000000..ca2926d6c825
--- /dev/null
+++ b/packages/eslint-plugin/docs/rules/keyword-spacing.md
@@ -0,0 +1,22 @@
+# Enforce consistent spacing before and after keywords (`keyword-spacing`)
+
+## Rule Details
+
+This rule extends the base [`eslint/keyword-spacing`](https://eslint.org/docs/rules/keyword-spacing) rule.
+This version adds support for generic type parameters on function calls.
+
+## How to use
+
+```cjson
+{
+ // note you must disable the base rule as it can report incorrect errors
+ "keyword-spacing": "off",
+ "@typescript-eslint/keyword-spacing": ["error"]
+}
+```
+
+## Options
+
+See [`eslint/keyword-spacing` options](https://eslint.org/docs/rules/keyword-spacing#options).
+
+Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/keyword-spacing.md)
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 5c476b51303d..569ffe0e6d97 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/docs/rules/no-floating-promises.md b/packages/eslint-plugin/docs/rules/no-floating-promises.md
index ffaa542f3f11..4ea2e6addc99 100644
--- a/packages/eslint-plugin/docs/rules/no-floating-promises.md
+++ b/packages/eslint-plugin/docs/rules/no-floating-promises.md
@@ -51,6 +51,8 @@ The rule accepts an options object with the following properties:
type Options = {
// if true, checking void expressions will be skipped
ignoreVoid?: boolean;
+ // if true, checking for async iife will be skipped
+ ignoreIIFE?: boolean;
};
const defaults = {
@@ -60,7 +62,8 @@ const defaults = {
### `ignoreVoid`
-This allows to easily suppress false-positives with void operator.
+This allows you to stop the rule reporting promises consumed with void operator.
+This can be a good way to explicitly mark a promise as intentionally not awaited.
Examples of **correct** code for this rule with `{ ignoreVoid: true }`:
@@ -73,10 +76,25 @@ void returnsPromise();
void Promise.reject('value');
```
+### `ignoreIIFE`
+
+This allows you to skip checking of async iife
+
+Examples of **correct** code for this rule with `{ ignoreIIFE: true }`:
+
+```ts
+await(async function() {
+ await res(1);
+})();
+
+(async function() {
+ await res(1);
+})();
+```
+
## When Not To Use It
-If you do not use Promise-like values in your codebase or want to allow them to
-remain unhandled.
+If you do not use Promise-like values in your codebase, or want to allow them to remain unhandled.
## Related to
diff --git a/packages/eslint-plugin/docs/rules/restrict-template-expressions.md b/packages/eslint-plugin/docs/rules/restrict-template-expressions.md
index 4c52e74445f5..816a0b0f9d65 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/package.json b/packages/eslint-plugin/package.json
index d872079514a8..29b9e0d308cd 100644
--- a/packages/eslint-plugin/package.json
+++ b/packages/eslint-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/eslint-plugin",
- "version": "2.28.0",
+ "version": "2.29.0",
"description": "TypeScript plugin for ESLint",
"keywords": [
"eslint",
@@ -41,7 +41,7 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/experimental-utils": "2.28.0",
+ "@typescript-eslint/experimental-utils": "2.29.0",
"functional-red-black-tree": "^1.0.1",
"regexpp": "^3.0.0",
"tsutils": "^3.17.1"
diff --git a/packages/eslint-plugin/src/configs/all.json b/packages/eslint-plugin/src/configs/all.json
index c736840a3fff..99e6060d6709 100644
--- a/packages/eslint-plugin/src/configs/all.json
+++ b/packages/eslint-plugin/src/configs/all.json
@@ -22,6 +22,10 @@
"@typescript-eslint/func-call-spacing": "error",
"indent": "off",
"@typescript-eslint/indent": "error",
+ "init-declarations": "off",
+ "@typescript-eslint/init-declarations": "error",
+ "keyword-spacing": "off",
+ "@typescript-eslint/keyword-spacing": "error",
"@typescript-eslint/member-delimiter-style": "error",
"@typescript-eslint/member-ordering": "error",
"@typescript-eslint/method-signature-style": "error",
diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts
index 901d31a8b9fb..cbc3b8c68f9d 100644
--- a/packages/eslint-plugin/src/rules/index.ts
+++ b/packages/eslint-plugin/src/rules/index.ts
@@ -19,6 +19,7 @@ import funcCallSpacing from './func-call-spacing';
import genericTypeNaming from './generic-type-naming';
import indent from './indent';
import interfaceNamePrefix from './interface-name-prefix';
+import keywordSpacing from './keyword-spacing';
import memberDelimiterStyle from './member-delimiter-style';
import memberNaming from './member-naming';
import memberOrdering from './member-ordering';
@@ -31,10 +32,10 @@ import noDynamicDelete from './no-dynamic-delete';
import noEmptyFunction from './no-empty-function';
import noEmptyInterface from './no-empty-interface';
import noExplicitAny from './no-explicit-any';
+import noExtraneousClass from './no-extraneous-class';
import noExtraNonNullAssertion from './no-extra-non-null-assertion';
import noExtraParens from './no-extra-parens';
import noExtraSemi from './no-extra-semi';
-import noExtraneousClass from './no-extraneous-class';
import noFloatingPromises from './no-floating-promises';
import noForInArray from './no-for-in-array';
import noImpliedEval from './no-implied-eval';
@@ -86,6 +87,7 @@ import requireAwait from './require-await';
import restrictPlusOperands from './restrict-plus-operands';
import restrictTemplateExpressions from './restrict-template-expressions';
import returnAwait from './return-await';
+import initDeclarations from './init-declarations';
import semi from './semi';
import spaceBeforeFunctionParen from './space-before-function-paren';
import strictBooleanExpressions from './strict-boolean-expressions';
@@ -117,7 +119,9 @@ export default {
'func-call-spacing': funcCallSpacing,
'generic-type-naming': genericTypeNaming,
indent: indent,
+ 'init-declarations': initDeclarations,
'interface-name-prefix': interfaceNamePrefix,
+ 'keyword-spacing': keywordSpacing,
'member-delimiter-style': memberDelimiterStyle,
'member-naming': memberNaming,
'member-ordering': memberOrdering,
diff --git a/packages/eslint-plugin/src/rules/init-declarations.ts b/packages/eslint-plugin/src/rules/init-declarations.ts
new file mode 100644
index 000000000000..b4368527e0cd
--- /dev/null
+++ b/packages/eslint-plugin/src/rules/init-declarations.ts
@@ -0,0 +1,53 @@
+import {
+ TSESTree,
+ AST_NODE_TYPES,
+} from '@typescript-eslint/experimental-utils';
+import baseRule from 'eslint/lib/rules/init-declarations';
+import {
+ InferOptionsTypeFromRule,
+ InferMessageIdsTypeFromRule,
+ createRule,
+} from '../util';
+
+export type Options = InferOptionsTypeFromRule;
+export type MessageIds = InferMessageIdsTypeFromRule;
+
+export default createRule({
+ name: 'init-declarations',
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description:
+ 'require or disallow initialization in variable declarations',
+ category: 'Variables',
+ recommended: false,
+ extendsBaseRule: true,
+ },
+ schema: baseRule.meta.schema,
+ messages: baseRule.meta.messages,
+ },
+ defaultOptions: ['always'],
+ create(context) {
+ const rules = baseRule.create(context);
+ const mode = context.options[0] || 'always';
+
+ return {
+ 'VariableDeclaration:exit'(node: TSESTree.VariableDeclaration): void {
+ if (mode === 'always') {
+ if (node.declare) {
+ return;
+ }
+ if (
+ node.parent?.type === AST_NODE_TYPES.TSModuleBlock &&
+ node.parent.parent?.type === AST_NODE_TYPES.TSModuleDeclaration &&
+ node.parent.parent?.declare
+ ) {
+ return;
+ }
+ }
+
+ rules['VariableDeclaration:exit'](node);
+ },
+ };
+ },
+});
diff --git a/packages/eslint-plugin/src/rules/keyword-spacing.ts b/packages/eslint-plugin/src/rules/keyword-spacing.ts
new file mode 100644
index 000000000000..8c4ff1d38ff6
--- /dev/null
+++ b/packages/eslint-plugin/src/rules/keyword-spacing.ts
@@ -0,0 +1,52 @@
+import { AST_TOKEN_TYPES } from '@typescript-eslint/experimental-utils';
+import baseRule from 'eslint/lib/rules/keyword-spacing';
+import * as util from '../util';
+
+export type Options = util.InferOptionsTypeFromRule;
+export type MessageIds = util.InferMessageIdsTypeFromRule;
+
+export default util.createRule({
+ name: 'keyword-spacing',
+ meta: {
+ type: 'layout',
+ docs: {
+ description: 'Enforce consistent spacing before and after keywords',
+ category: 'Stylistic Issues',
+ recommended: false,
+ extendsBaseRule: true,
+ },
+ fixable: 'whitespace',
+ schema: baseRule.meta.schema,
+ messages: baseRule.meta.messages,
+ },
+ defaultOptions: [{}],
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ const baseRules = baseRule.create(context);
+ return {
+ ...baseRules,
+ TSAsExpression(node): void {
+ const asToken = util.nullThrows(
+ sourceCode.getTokenAfter(
+ node.expression,
+ token => token.value === 'as',
+ ),
+ util.NullThrowsReasons.MissingToken('as', node.type),
+ );
+ const oldTokenType = asToken.type;
+ // as is a contextual keyword, so it's always reported as an Identifier
+ // the rule looks for keyword tokens, so we temporarily override it
+ // we mutate it at the token level because the rule calls sourceCode.getFirstToken,
+ // so mutating a copy would not change the underlying copy returned by that method
+ asToken.type = AST_TOKEN_TYPES.Keyword;
+
+ // use this selector just because it is just a call to `checkSpacingAroundFirstToken`
+ baseRules.DebuggerStatement(asToken as never);
+
+ // make sure to reset the type afterward so we don't permanently mutate the AST
+ asToken.type = oldTokenType;
+ },
+ };
+ },
+});
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 47803f8e2aa4..a1121f37abd7 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/src/rules/no-floating-promises.ts b/packages/eslint-plugin/src/rules/no-floating-promises.ts
index c3ee7ffccf3c..09d70bac42c0 100644
--- a/packages/eslint-plugin/src/rules/no-floating-promises.ts
+++ b/packages/eslint-plugin/src/rules/no-floating-promises.ts
@@ -1,12 +1,17 @@
import * as tsutils from 'tsutils';
import * as ts from 'typescript';
-import { TSESLint } from '@typescript-eslint/experimental-utils';
+import {
+ TSESLint,
+ AST_NODE_TYPES,
+ TSESTree,
+} from '@typescript-eslint/experimental-utils';
import * as util from '../util';
type Options = [
{
ignoreVoid?: boolean;
+ ignoreIIFE?: boolean;
},
];
@@ -33,6 +38,7 @@ export default util.createRule({
type: 'object',
properties: {
ignoreVoid: { type: 'boolean' },
+ ignoreIIFE: { type: 'boolean' },
},
additionalProperties: false,
},
@@ -42,6 +48,7 @@ export default util.createRule({
defaultOptions: [
{
ignoreVoid: false,
+ ignoreIIFE: false,
},
],
@@ -54,6 +61,10 @@ export default util.createRule({
ExpressionStatement(node): void {
const { expression } = parserServices.esTreeNodeToTSNodeMap.get(node);
+ if (options.ignoreIIFE && isAsyncIife(node)) {
+ return;
+ }
+
if (isUnhandledPromise(checker, expression)) {
if (options.ignoreVoid) {
context.report({
@@ -80,6 +91,19 @@ export default util.createRule({
},
};
+ function isAsyncIife(node: TSESTree.ExpressionStatement): boolean {
+ if (node.expression.type !== AST_NODE_TYPES.CallExpression) {
+ return false;
+ }
+
+ return (
+ node.expression.type === AST_NODE_TYPES.CallExpression &&
+ (node.expression.callee.type ===
+ AST_NODE_TYPES.ArrowFunctionExpression ||
+ node.expression.callee.type === AST_NODE_TYPES.FunctionExpression)
+ );
+ }
+
function isUnhandledPromise(
checker: ts.TypeChecker,
node: ts.Node,
diff --git a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts
index d73d8d98a254..90dd363f8204 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/init-declarations.test.ts b/packages/eslint-plugin/tests/rules/init-declarations.test.ts
new file mode 100644
index 000000000000..6f4c8f75ebbc
--- /dev/null
+++ b/packages/eslint-plugin/tests/rules/init-declarations.test.ts
@@ -0,0 +1,728 @@
+import { AST_NODE_TYPES } from '@typescript-eslint/experimental-utils';
+import rule from '../../src/rules/init-declarations';
+import { RuleTester } from '../RuleTester';
+
+const ruleTester = new RuleTester({
+ parser: '@typescript-eslint/parser',
+});
+
+ruleTester.run('init-declarations', rule, {
+ valid: [
+ // checking compatibility with base rule
+ 'var foo = null;',
+ 'foo = true;',
+ `
+var foo = 1,
+ bar = false,
+ baz = {};
+ `,
+ `
+function foo() {
+ var foo = 0;
+ var bar = [];
+}
+ `,
+ 'var fn = function() {};',
+ 'var foo = (bar = 2);',
+ 'for (var i = 0; i < 1; i++) {}',
+ `
+for (var foo in []) {
+}
+ `,
+ {
+ code: `
+for (var foo of []) {
+}
+ `,
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'let a = true;',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'const a = {};',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: `
+function foo() {
+ let a = 1,
+ b = false;
+ if (a) {
+ let c = 3,
+ d = null;
+ }
+}
+ `,
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: `
+function foo() {
+ const a = 1,
+ b = true;
+ if (a) {
+ const c = 3,
+ d = null;
+ }
+}
+ `,
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: `
+function foo() {
+ let a = 1;
+ const b = false;
+ var c = true;
+}
+ `,
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var foo;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var foo, bar, baz;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: `
+function foo() {
+ var foo;
+ var bar;
+}
+ `,
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'let a;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'const a = 1;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: `
+function foo() {
+ let a, b;
+ if (a) {
+ let c, d;
+ }
+}
+ `,
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: `
+function foo() {
+ const a = 1,
+ b = true;
+ if (a) {
+ const c = 3,
+ d = null;
+ }
+}
+ `,
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: `
+function foo() {
+ let a;
+ const b = false;
+ var c;
+}
+ `,
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'for (var i = 0; i < 1; i++) {}',
+ options: ['never', { ignoreForLoopInit: true }],
+ },
+ {
+ code: `
+for (var foo in []) {
+}
+ `,
+ options: ['never', { ignoreForLoopInit: true }],
+ },
+ {
+ code: `
+for (var foo of []) {
+}
+ `,
+ options: ['never', { ignoreForLoopInit: true }],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: `
+function foo() {
+ var bar = 1;
+ let baz = 2;
+ const qux = 3;
+}
+ `,
+ options: ['always'],
+ },
+
+ // typescript-eslint
+ {
+ code: 'declare const foo: number;',
+ options: ['always'],
+ },
+ {
+ code: 'declare const foo: number;',
+ options: ['never'],
+ },
+ {
+ code: `
+declare namespace myLib {
+ let numberOfGreetings: number;
+}
+ `,
+ options: ['always'],
+ },
+ {
+ code: `
+declare namespace myLib {
+ let numberOfGreetings: number;
+}
+ `,
+ options: ['never'],
+ },
+ {
+ code: `
+interface GreetingSettings {
+ greeting: string;
+ duration?: number;
+ color?: string;
+}
+ `,
+ },
+ {
+ code: `
+interface GreetingSettings {
+ greeting: string;
+ duration?: number;
+ color?: string;
+}
+ `,
+ options: ['never'],
+ },
+ 'type GreetingLike = string | (() => string) | Greeter;',
+ {
+ code: 'type GreetingLike = string | (() => string) | Greeter;',
+ options: ['never'],
+ },
+ {
+ code: `
+function foo() {
+ var bar: string;
+}
+ `,
+ options: ['never'],
+ },
+ {
+ code: 'var bar: string;',
+ options: ['never'],
+ },
+ {
+ code: `
+var bar: string = function(): string {
+ return 'string';
+};
+ `,
+ options: ['always'],
+ },
+ {
+ code: `
+var bar: string = function(arg1: stirng): string {
+ return 'string';
+};
+ `,
+ options: ['always'],
+ },
+ {
+ code: "function foo(arg1: string = 'string'): void {}",
+ options: ['never'],
+ },
+ {
+ code: "const foo: string = 'hello';",
+ options: ['never'],
+ },
+ {
+ code: `
+const class1 = class NAME {
+ constructor() {
+ var name1: string = 'hello';
+ }
+};
+ `,
+ },
+ {
+ code: `
+const class1 = class NAME {
+ static pi: number = 3.14;
+};
+ `,
+ },
+ {
+ code: `
+const class1 = class NAME {
+ static pi: number = 3.14;
+};
+ `,
+ options: ['never'],
+ },
+ {
+ code: `
+interface IEmployee {
+ empCode: number;
+ empName: string;
+ getSalary: (number) => number; // arrow function
+ getManagerName(number): string;
+}
+ `,
+ },
+ {
+ code: `
+interface IEmployee {
+ empCode: number;
+ empName: string;
+ getSalary: (number) => number; // arrow function
+ getManagerName(number): string;
+}
+ `,
+ options: ['never'],
+ },
+ {
+ code: "declare const foo: number = 'asd';",
+ options: ['always'],
+ },
+
+ {
+ code: "const foo: number = 'asd';",
+ options: ['always'],
+ },
+ {
+ code: 'const foo: number;',
+ options: ['never'],
+ },
+ {
+ code: `
+namespace myLib {
+ let numberOfGreetings: number;
+}
+ `,
+ options: ['never'],
+ },
+ {
+ code: `
+namespace myLib {
+ let numberOfGreetings: number = 2;
+}
+ `,
+ options: ['always'],
+ },
+ ],
+ invalid: [
+ // checking compatibility with base rule
+ {
+ code: 'var foo;',
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'initialized',
+ data: { idName: 'foo' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: 'for (var a in []) var foo;',
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'initialized',
+ data: { idName: 'foo' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: `
+var foo,
+ bar = false,
+ baz;
+ `,
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'initialized',
+ data: { idName: 'foo' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ {
+ messageId: 'initialized',
+ data: { idName: 'baz' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: `
+function foo() {
+ var foo = 0;
+ var bar;
+}
+ `,
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'initialized',
+ data: { idName: 'bar' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: `
+function foo() {
+ var foo;
+ var bar = foo;
+}
+ `,
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'initialized',
+ data: { idName: 'foo' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: 'let a;',
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'initialized',
+ data: { idName: 'a' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: `
+function foo() {
+ let a = 1,
+ b;
+ if (a) {
+ let c = 3,
+ d = null;
+ }
+}
+ `,
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'initialized',
+ data: { idName: 'b' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: `
+function foo() {
+ let a;
+ const b = false;
+ var c;
+}
+ `,
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'initialized',
+ data: { idName: 'a' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ {
+ messageId: 'initialized',
+ data: { idName: 'c' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: 'var foo = (bar = 2);',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'notInitialized',
+ data: { idName: 'foo' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: 'var foo = true;',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'notInitialized',
+ data: { idName: 'foo' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: `
+var foo,
+ bar = 5,
+ baz = 3;
+ `,
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'notInitialized',
+ data: { idName: 'bar' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ {
+ messageId: 'notInitialized',
+ data: { idName: 'baz' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: `
+function foo() {
+ var foo;
+ var bar = foo;
+}
+ `,
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'notInitialized',
+ data: { idName: 'bar' },
+
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: 'let a = 1;',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'notInitialized',
+ data: { idName: 'a' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: `
+function foo() {
+ let a = 'foo',
+ b;
+ if (a) {
+ let c, d;
+ }
+}
+ `,
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'notInitialized',
+ data: { idName: 'a' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: `
+function foo() {
+ let a;
+ const b = false;
+ var c = 1;
+}
+ `,
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'notInitialized',
+ data: { idName: 'c' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: 'for (var i = 0; i < 1; i++) {}',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'notInitialized',
+ data: { idName: 'i' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: `
+for (var foo in []) {
+}
+ `,
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'notInitialized',
+ data: { idName: 'foo' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: `
+for (var foo of []) {
+}
+ `,
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'notInitialized',
+ data: { idName: 'foo' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: `
+function foo() {
+ var bar;
+}
+ `,
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'initialized',
+ data: { idName: 'bar' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+
+ // typescript-eslint
+ {
+ code: "let arr: string[] = ['arr', 'ar'];",
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'notInitialized',
+ data: { idName: 'arr' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: 'let arr: string = function() {};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'notInitialized',
+ data: { idName: 'arr' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: `
+const class1 = class NAME {
+ constructor() {
+ var name1: string = 'hello';
+ }
+};
+ `,
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'notInitialized',
+ data: { idName: 'name1' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: 'let arr: string;',
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'initialized',
+ data: { idName: 'arr' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: "declare var foo: number = 'asd';",
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'notInitialized',
+ data: { idName: 'foo' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: `
+namespace myLib {
+ let numberOfGreetings: number;
+}
+ `,
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'initialized',
+ data: { idName: 'numberOfGreetings' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ {
+ code: `
+namespace myLib {
+ let numberOfGreetings: number = 2;
+}
+ `,
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'notInitialized',
+ data: { idName: 'numberOfGreetings' },
+ type: AST_NODE_TYPES.VariableDeclarator,
+ },
+ ],
+ },
+ ],
+});
diff --git a/packages/eslint-plugin/tests/rules/keyword-spacing.test.ts b/packages/eslint-plugin/tests/rules/keyword-spacing.test.ts
new file mode 100644
index 000000000000..987caebb1e43
--- /dev/null
+++ b/packages/eslint-plugin/tests/rules/keyword-spacing.test.ts
@@ -0,0 +1,153 @@
+/* eslint-disable eslint-comments/no-use */
+// this rule tests the spacing, which prettier will want to fix and break the tests
+/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */
+/* eslint-enable eslint-comments/no-use */
+import { TSESLint } from '@typescript-eslint/experimental-utils';
+import rule, { MessageIds, Options } from '../../src/rules/keyword-spacing';
+import { RuleTester } from '../RuleTester';
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const BOTH = { before: true, after: true };
+const NEITHER = { before: false, after: false };
+
+/**
+ * Creates an option object to test an 'overrides' option.
+ *
+ * e.g.
+ *
+ * override('as', BOTH)
+ *
+ * returns
+ *
+ * {
+ * before: false,
+ * after: false,
+ * overrides: {as: {before: true, after: true}}
+ * }
+ * @param keyword A keyword to be overridden.
+ * @param value A value to override.
+ * @returns An option object to test an 'overrides' option.
+ */
+function overrides(keyword: string, value: Options[0]): Options[0] {
+ return {
+ before: value.before === false,
+ after: value.after === false,
+ overrides: { [keyword]: value },
+ };
+}
+
+/**
+ * Gets an error message that expected space(s) before a specified keyword.
+ * @param keyword A keyword.
+ * @returns An error message.
+ */
+function expectedBefore(keyword: string): TSESLint.TestCaseError[] {
+ return [{ messageId: 'expectedBefore', data: { value: keyword } }];
+}
+
+/**
+ * Gets an error message that expected space(s) after a specified keyword.
+ * @param keyword A keyword.
+ * @returns An error message.
+ */
+function expectedAfter(keyword: string): TSESLint.TestCaseError[] {
+ return [{ messageId: 'expectedAfter', data: { value: keyword } }];
+}
+
+/**
+ * Gets an error message that unexpected space(s) before a specified keyword.
+ * @param keyword A keyword.
+ * @returns An error message.
+ */
+function unexpectedBefore(
+ keyword: string,
+): TSESLint.TestCaseError[] {
+ return [{ messageId: 'unexpectedBefore', data: { value: keyword } }];
+}
+
+/**
+ * Gets an error message that unexpected space(s) after a specified keyword.
+ * @param keyword A keyword.
+ * @returns An error message.
+ */
+function unexpectedAfter(
+ keyword: string,
+): TSESLint.TestCaseError[] {
+ return [{ messageId: 'unexpectedAfter', data: { value: keyword } }];
+}
+
+const ruleTester = new RuleTester({
+ parser: '@typescript-eslint/parser',
+});
+
+ruleTester.run('keyword-spacing', rule, {
+ valid: [
+ //----------------------------------------------------------------------
+ // as (typing)
+ //----------------------------------------------------------------------
+ {
+ code: 'const foo = {} as {};',
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'const foo = {}as{};',
+ options: [NEITHER],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'const foo = {} as {};',
+ options: [overrides('as', BOTH)],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'const foo = {}as{};',
+ options: [overrides('as', NEITHER)],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'const foo = {} as {};',
+ options: [{ overrides: { as: {} } }],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ ],
+ invalid: [
+ //----------------------------------------------------------------------
+ // as (typing)
+ //----------------------------------------------------------------------
+ {
+ code: 'const foo = {}as {};',
+ output: 'const foo = {} as {};',
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: expectedBefore('as'),
+ },
+ {
+ code: 'const foo = {} as{};',
+ output: 'const foo = {}as{};',
+ options: [NEITHER],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: unexpectedBefore('as'),
+ },
+ {
+ code: 'const foo = {} as{};',
+ output: 'const foo = {} as {};',
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: expectedAfter('as'),
+ },
+ {
+ code: 'const foo = {}as {};',
+ output: 'const foo = {}as{};',
+ options: [NEITHER],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: unexpectedAfter('as'),
+ },
+ {
+ code: 'const foo = {} as{};',
+ options: [{ overrides: { as: {} } }],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: expectedAfter('as'),
+ },
+ ],
+});
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 59abfbb1f602..82bd4e0302e1 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: [
diff --git a/packages/eslint-plugin/tests/rules/no-floating-promises.test.ts b/packages/eslint-plugin/tests/rules/no-floating-promises.test.ts
index c7e688c9852e..f6601a0613a1 100644
--- a/packages/eslint-plugin/tests/rules/no-floating-promises.test.ts
+++ b/packages/eslint-plugin/tests/rules/no-floating-promises.test.ts
@@ -335,6 +335,54 @@ async function test() {
return returnsPromise();
}
`,
+ // ignoreIIFE
+ {
+ code: `
+ (async () => {
+ await something();
+ })();
+ `,
+ options: [{ ignoreIIFE: true }],
+ },
+ {
+ code: `
+ (async () => {
+ something();
+ })();
+ `,
+ options: [{ ignoreIIFE: true }],
+ },
+ {
+ code: '(async function foo() {})();',
+ options: [{ ignoreIIFE: true }],
+ },
+ {
+ code: `
+ function foo() {
+ (async function bar() {})();
+ }
+ `,
+ options: [{ ignoreIIFE: true }],
+ },
+ {
+ code: `
+ const foo = () =>
+ new Promise(res => {
+ (async function() {
+ await res(1);
+ })();
+ });
+ `,
+ options: [{ ignoreIIFE: true }],
+ },
+ {
+ code: `
+ (async function() {
+ await res(1);
+ })();
+ `,
+ options: [{ ignoreIIFE: true }],
+ },
],
invalid: [
@@ -726,5 +774,126 @@ async function test() {
},
],
},
+ {
+ code: `
+ (async () => {
+ await something();
+ })();
+ `,
+ errors: [
+ {
+ line: 2,
+ messageId: 'floating',
+ },
+ ],
+ },
+ {
+ code: `
+ (async () => {
+ something();
+ })();
+ `,
+ errors: [
+ {
+ line: 2,
+ messageId: 'floating',
+ },
+ ],
+ },
+ {
+ code: '(async function foo() {})();',
+ errors: [
+ {
+ line: 1,
+ messageId: 'floating',
+ },
+ ],
+ },
+ {
+ code: `
+ function foo() {
+ (async function bar() {})();
+ }
+ `,
+ errors: [
+ {
+ line: 3,
+ messageId: 'floating',
+ },
+ ],
+ },
+ {
+ code: `
+ const foo = () =>
+ new Promise(res => {
+ (async function() {
+ await res(1);
+ })();
+ });
+ `,
+ errors: [
+ {
+ line: 4,
+ messageId: 'floating',
+ },
+ ],
+ },
+ {
+ code: `
+ (async function() {
+ await res(1);
+ })();
+ `,
+ errors: [
+ {
+ line: 2,
+ messageId: 'floating',
+ },
+ ],
+ },
+ {
+ code: `
+ (async function() {
+ Promise.resolve();
+ })();
+ `,
+ options: [{ ignoreIIFE: true }],
+ errors: [
+ {
+ line: 3,
+ messageId: 'floating',
+ },
+ ],
+ },
+ {
+ code: `
+ (async function() {
+ const promiseIntersection: Promise & number;
+ promiseIntersection;
+ promiseIntersection.then(() => {});
+ promiseIntersection.catch();
+ promiseIntersection.finally();
+ })();
+ `,
+ options: [{ ignoreIIFE: true }],
+ errors: [
+ {
+ line: 4,
+ messageId: 'floating',
+ },
+ {
+ line: 5,
+ messageId: 'floating',
+ },
+ {
+ line: 6,
+ messageId: 'floating',
+ },
+ {
+ line: 7,
+ messageId: 'floating',
+ },
+ ],
+ },
],
});
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 6a2861efa323..2f5900f3d2ac 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: `
diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts
index ea60d9b31697..eb2d1cd9c60e 100644
--- a/packages/eslint-plugin/typings/eslint-rules.d.ts
+++ b/packages/eslint-plugin/typings/eslint-rules.d.ts
@@ -142,6 +142,85 @@ declare module 'eslint/lib/rules/indent' {
export = rule;
}
+declare module 'eslint/lib/rules/keyword-spacing' {
+ import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils';
+ import { RuleFunction } from '@typescript-eslint/experimental-utils/dist/ts-eslint';
+
+ type Options = [
+ {
+ before?: boolean;
+ after?: boolean;
+ overrides?: Record<
+ string,
+ {
+ before?: boolean;
+ after?: boolean;
+ }
+ >;
+ },
+ ];
+ type MessageIds =
+ | 'expectedBefore'
+ | 'expectedAfter'
+ | 'unexpectedBefore'
+ | 'unexpectedAfter';
+
+ const rule: TSESLint.RuleModule<
+ MessageIds,
+ Options,
+ {
+ // Statements
+ DebuggerStatement: RuleFunction;
+ WithStatement: RuleFunction;
+
+ // Statements - Control flow
+ BreakStatement: RuleFunction;
+ ContinueStatement: RuleFunction;
+ ReturnStatement: RuleFunction;
+ ThrowStatement: RuleFunction;
+ TryStatement: RuleFunction;
+
+ // Statements - Choice
+ IfStatement: RuleFunction;
+ SwitchStatement: RuleFunction;
+ SwitchCase: RuleFunction;
+
+ // Statements - Loops
+ DoWhileStatement: RuleFunction;
+ ForInStatement: RuleFunction;
+ ForOfStatement: RuleFunction;
+ ForStatement: RuleFunction;
+ WhileStatement: RuleFunction;
+
+ // Statements - Declarations
+ ClassDeclaration: RuleFunction;
+ ExportNamedDeclaration: RuleFunction;
+ ExportDefaultDeclaration: RuleFunction;
+ ExportAllDeclaration: RuleFunction;
+ FunctionDeclaration: RuleFunction;
+ ImportDeclaration: RuleFunction;
+ VariableDeclaration: RuleFunction;
+
+ // Expressions
+ ArrowFunctionExpression: RuleFunction;
+ AwaitExpression: RuleFunction;
+ ClassExpression: RuleFunction;
+ FunctionExpression: RuleFunction;
+ NewExpression: RuleFunction;
+ Super: RuleFunction;
+ ThisExpression: RuleFunction;
+ UnaryExpression: RuleFunction;
+ YieldExpression: RuleFunction;
+
+ // Others
+ ImportNamespaceSpecifier: RuleFunction;
+ MethodDefinition: RuleFunction;
+ Property: RuleFunction;
+ }
+ >;
+ export = rule;
+}
+
declare module 'eslint/lib/rules/no-dupe-class-members' {
import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils';
@@ -543,3 +622,21 @@ declare module 'eslint/lib/rules/no-extra-semi' {
>;
export = rule;
}
+
+declare module 'eslint/lib/rules/init-declarations' {
+ import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils';
+
+ const rule: TSESLint.RuleModule<
+ 'initialized' | 'notInitialized',
+ [
+ 'always' | 'never',
+ {
+ ignoreForLoopInit?: boolean;
+ }?,
+ ],
+ {
+ 'VariableDeclaration:exit'(node: TSESTree.VariableDeclaration): void;
+ }
+ >;
+ export = rule;
+}
diff --git a/packages/experimental-utils/CHANGELOG.md b/packages/experimental-utils/CHANGELOG.md
index ece5fcbb544c..b1f38a523313 100644
--- a/packages/experimental-utils/CHANGELOG.md
+++ b/packages/experimental-utils/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.29.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.28.0...v2.29.0) (2020-04-20)
+
+**Note:** Version bump only for package @typescript-eslint/experimental-utils
+
+
+
+
+
# [2.28.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.27.0...v2.28.0) (2020-04-13)
diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json
index b623c12ed314..ac9a07b11c45 100644
--- a/packages/experimental-utils/package.json
+++ b/packages/experimental-utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/experimental-utils",
- "version": "2.28.0",
+ "version": "2.29.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.28.0",
+ "@typescript-eslint/typescript-estree": "2.29.0",
"eslint-scope": "^5.0.0",
"eslint-utils": "^2.0.0"
},
diff --git a/packages/parser/CHANGELOG.md b/packages/parser/CHANGELOG.md
index c99a618a07ef..74cb4ee49709 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.29.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.28.0...v2.29.0) (2020-04-20)
+
+**Note:** Version bump only for package @typescript-eslint/parser
+
+
+
+
+
# [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
diff --git a/packages/parser/package.json b/packages/parser/package.json
index a36625794ad1..8aa5d8930483 100644
--- a/packages/parser/package.json
+++ b/packages/parser/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/parser",
- "version": "2.28.0",
+ "version": "2.29.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.28.0",
- "@typescript-eslint/typescript-estree": "2.28.0",
+ "@typescript-eslint/experimental-utils": "2.29.0",
+ "@typescript-eslint/typescript-estree": "2.29.0",
"eslint-visitor-keys": "^1.1.0"
},
"devDependencies": {
"@types/glob": "^7.1.1",
- "@typescript-eslint/shared-fixtures": "2.28.0",
+ "@typescript-eslint/shared-fixtures": "2.29.0",
"glob": "*"
},
"peerDependenciesMeta": {
diff --git a/packages/shared-fixtures/CHANGELOG.md b/packages/shared-fixtures/CHANGELOG.md
index f460b702eb95..181fc307a111 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.29.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.28.0...v2.29.0) (2020-04-20)
+
+**Note:** Version bump only for package @typescript-eslint/shared-fixtures
+
+
+
+
+
# [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
diff --git a/packages/shared-fixtures/package.json b/packages/shared-fixtures/package.json
index e843de63a56b..610efbaabd73 100644
--- a/packages/shared-fixtures/package.json
+++ b/packages/shared-fixtures/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/shared-fixtures",
- "version": "2.28.0",
+ "version": "2.29.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 8750d8a65ed5..9ea9a9c6ec7a 100644
--- a/packages/typescript-estree/CHANGELOG.md
+++ b/packages/typescript-estree/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.29.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.28.0...v2.29.0) (2020-04-20)
+
+
+### Features
+
+* **eslint-plugin:** [no-floating-promise] add option to ignore IIFEs ([#1799](https://github.com/typescript-eslint/typescript-eslint/issues/1799)) ([cea51bf](https://github.com/typescript-eslint/typescript-eslint/commit/cea51bf130d6d3c2935f5e2dcc468196f2ad9d00))
+
+
+
+
+
# [2.28.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.27.0...v2.28.0) (2020-04-13)
diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json
index bc75ebdf786c..8f69a2a71a0b 100644
--- a/packages/typescript-estree/package.json
+++ b/packages/typescript-estree/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/typescript-estree",
- "version": "2.28.0",
+ "version": "2.29.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.28.0",
+ "@typescript-eslint/shared-fixtures": "2.29.0",
"tmp": "^0.1.0",
"typescript": "*"
},
diff --git a/packages/typescript-estree/src/ts-estree/ts-estree.ts b/packages/typescript-estree/src/ts-estree/ts-estree.ts
index 5792ebc02058..1781ffd5fff1 100644
--- a/packages/typescript-estree/src/ts-estree/ts-estree.ts
+++ b/packages/typescript-estree/src/ts-estree/ts-estree.ts
@@ -392,7 +392,8 @@ export type LeftHandSideExpression =
| PrimaryExpression
| TaggedTemplateExpression
| TSNonNullExpression
- | TSAsExpression;
+ | TSAsExpression
+ | ArrowFunctionExpression;
export type Literal =
| BooleanLiteral
| NumberLiteral