From 6bb84bdb29ad63b49a99a3e314d5d5b76d85abe0 Mon Sep 17 00:00:00 2001 From: Jeff Principe Date: Sat, 4 May 2019 22:47:33 -0700 Subject: [PATCH 1/5] feat(eslint-plugin): add new rule no-floating-promises Adds the equivalent of TSLint's `no-floating-promises` rule. Fixes #464 --- packages/eslint-plugin/README.md | 1 + packages/eslint-plugin/ROADMAP.md | 5 +- .../docs/rules/no-floating-promises.md | 46 ++ .../src/rules/no-floating-promises.ts | 173 ++++++ .../tests/rules/no-floating-promises.test.ts | 549 ++++++++++++++++++ 5 files changed, 772 insertions(+), 2 deletions(-) create mode 100644 packages/eslint-plugin/docs/rules/no-floating-promises.md create mode 100644 packages/eslint-plugin/src/rules/no-floating-promises.ts create mode 100644 packages/eslint-plugin/tests/rules/no-floating-promises.test.ts diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 044ff0643727..cd4ca274d7f5 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -132,6 +132,7 @@ Then you should add `airbnb` (or `airbnb-base`) to your `extends` section of `.e | [`@typescript-eslint/no-explicit-any`](./docs/rules/no-explicit-any.md) | Disallow usage of the `any` type (`no-any` from TSLint) | :heavy_check_mark: | | | | [`@typescript-eslint/no-extra-parens`](./docs/rules/no-extra-parens.md) | Disallow unnecessary parentheses | | :wrench: | | | [`@typescript-eslint/no-extraneous-class`](./docs/rules/no-extraneous-class.md) | Forbids the use of classes as namespaces (`no-unnecessary-class` from TSLint) | | | | +| [`@typescript-eslint/no-floating-promises`](./docs/rules/no-floating-promises.md) | Requires Promise-like values to be handled appropriately. | | | :thought_balloon: | | [`@typescript-eslint/no-for-in-array`](./docs/rules/no-for-in-array.md) | Disallow iterating over an array with a for-in loop (`no-for-in-array` from TSLint) | | | :thought_balloon: | | [`@typescript-eslint/no-inferrable-types`](./docs/rules/no-inferrable-types.md) | Disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean. (`no-inferrable-types` from TSLint) | :heavy_check_mark: | :wrench: | | | [`@typescript-eslint/no-misused-new`](./docs/rules/no-misused-new.md) | Enforce valid definition of `new` and `constructor`. (`no-misused-new` from TSLint) | :heavy_check_mark: | | | diff --git a/packages/eslint-plugin/ROADMAP.md b/packages/eslint-plugin/ROADMAP.md index b2567afe070e..f0171f73b090 100644 --- a/packages/eslint-plugin/ROADMAP.md +++ b/packages/eslint-plugin/ROADMAP.md @@ -1,4 +1,4 @@ -# Roadmap +# Roadmap ✅ = done
🌟 = in ESLint core
@@ -60,7 +60,7 @@ | [`no-dynamic-delete`] | 🛑 | N/A | | [`no-empty`] | 🌟 | [`no-empty`][no-empty] | | [`no-eval`] | 🌟 | [`no-eval`][no-eval] | -| [`no-floating-promises`] | 🛑 | N/A ([relevant plugin][plugin:promise]) | +| [`no-floating-promises`] | ✅ | [`@typescript-eslint/no-floating-promises`] | | [`no-for-in-array`] | ✅ | [`@typescript-eslint/no-for-in-array`] | | [`no-implicit-dependencies`] | 🔌 | [`import/no-extraneous-dependencies`] | | [`no-inferred-empty-object-type`] | 🛑 | N/A | @@ -612,6 +612,7 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint- [`@typescript-eslint/no-for-in-array`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-for-in-array.md [`@typescript-eslint/no-unnecessary-qualifier`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unnecessary-qualifier.md [`@typescript-eslint/semi`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/semi.md +[`@typescript-eslint/no-floating-promises`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-floating-promises.md diff --git a/packages/eslint-plugin/docs/rules/no-floating-promises.md b/packages/eslint-plugin/docs/rules/no-floating-promises.md new file mode 100644 index 000000000000..75bc49efa66d --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-floating-promises.md @@ -0,0 +1,46 @@ +# Requires Promise-like values to be handled appropriately (no-floating-promises) + +This rule forbids usage of Promise-like values in statements without handling +their errors appropriately. Unhandled promises can cause several issues, such +as improperly sequenced operations, ignored Promise rejections and more. Valid +ways of handling a Promise-valued statement include `await`ing, returning, and +either calling `.then()` with two arguments or `.catch()` with one argument. + +## Rule Details + +Examples of **incorrect** code for this rule: + +```ts +const promise = new Promise((resolve, reject) => resolve('value')); +promise; + +async function returnsPromise() { + return 'value'; +} +returnsPromise().then(() => {}); + +Promise.reject('value').catch(); +``` + +Examples of **correct** code for this rule: + +```ts +const promise = new Promise((resolve, reject) => resolve('value')); +await promise; + +async function returnsPromise() { + return 'value'; +} +returnsPromise().then(() => {}, () => {}); + +Promise.reject('value').catch(() => {}); +``` + +## When Not To Use It + +If you do not use Promise-like values in your codebase or want to allow them to +remain unhandled. + +## Related to + +- Tslint: ['no-floating-promises'](https://palantir.github.io/tslint/rules/no-floating-promises/) diff --git a/packages/eslint-plugin/src/rules/no-floating-promises.ts b/packages/eslint-plugin/src/rules/no-floating-promises.ts new file mode 100644 index 000000000000..2ab6a631d6f0 --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-floating-promises.ts @@ -0,0 +1,173 @@ +import * as tsutils from 'tsutils'; +import * as ts from 'typescript'; + +import * as util from '../util'; + +export default util.createRule({ + name: 'no-floating-promises', + meta: { + docs: { + description: + 'Requires promises to be awaited or have a rejection handler.', + category: 'Best Practices', + recommended: false, + tslintName: 'no-floating-promises', + }, + messages: { + floating: 'Promises must be handled appropriately', + }, + schema: [], + type: 'problem', + }, + defaultOptions: [], + + create(context) { + const parserServices = util.getParserServices(context); + const checker = parserServices.program.getTypeChecker(); + + return { + ExpressionStatement(node) { + const { expression } = parserServices.esTreeNodeToTSNodeMap.get( + node, + ) as ts.ExpressionStatement; + + if (isUnhandledPromise(checker, expression)) { + context.report({ + messageId: 'floating', + node, + }); + } + }, + }; + }, +}); + +function isUnhandledPromise(checker: ts.TypeChecker, node: ts.Node): boolean { + // First, check expressions whose resulting types may not be promise-like + if ( + ts.isBinaryExpression(node) && + node.operatorToken.kind === ts.SyntaxKind.CommaToken + ) { + // Any child in a comma expression could return a potentially unhandled + // promise, so we check them all regardless of whether the final returned + // value is promise-like. + return ( + isUnhandledPromise(checker, node.left) || + isUnhandledPromise(checker, node.right) + ); + } else if (ts.isVoidExpression(node)) { + // Similarly, a `void` expression always returns undefined, so we need to + // see what's inside it without checking the type of the overall expression. + return isUnhandledPromise(checker, node.expression); + } + + // Check the type. At this point it can't be unhandled if it isn't a promise + if (!isPromiseLike(checker, node)) { + return false; + } + + if (ts.isCallExpression(node)) { + // If the outer expression is a call, it must be either a `.then()` or + // `.catch()` that handles the promise. + return ( + !isPromiseCatchCallWithHandler(node) && + !isPromiseThenCallWithRejectionHandler(node) + ); + } else if (ts.isConditionalExpression(node)) { + // We must be getting the promise-like value from one of the branches of the + // ternary. Check them directly. + return ( + isUnhandledPromise(checker, node.whenFalse) || + isUnhandledPromise(checker, node.whenTrue) + ); + } else if ( + ts.isPropertyAccessExpression(node) || + ts.isIdentifier(node) || + ts.isNewExpression(node) + ) { + // If it is just a property access chain or a `new` call (e.g. `foo.bar` or + // `new Promise()`), the promise is not handled because it doesn't have the + // necessary then/catch call at the end of the chain. + return true; + } + + // We conservatively return false for all other types of expressions because + // we don't want to accidentally fail if the promise is handled internally but + // we just can't tell. + return false; +} + +// Modified from tsutils.isThenable() to only consider thenables which can be +// rejected/caught via a second parameter. Original source (MIT licensed): +// +// https://github.com/ajafff/tsutils/blob/49d0d31050b44b81e918eae4fbaf1dfe7b7286af/util/type.ts#L95-L125 +function isPromiseLike(checker: ts.TypeChecker, node: ts.Node): boolean { + const type = checker.getTypeAtLocation(node); + for (const ty of tsutils.unionTypeParts(checker.getApparentType(type))) { + const then = ty.getProperty('then'); + if (then === undefined) { + continue; + } + + const thenType = checker.getTypeOfSymbolAtLocation(then, node); + if ( + hasMatchingSignature( + thenType, + signature => + signature.parameters.length >= 2 && + isFunctionParam(checker, signature.parameters[0], node) && + isFunctionParam(checker, signature.parameters[1], node), + ) + ) { + return true; + } + } + return false; +} + +function hasMatchingSignature( + type: ts.Type, + matcher: (signature: ts.Signature) => boolean, +): boolean { + for (const t of tsutils.unionTypeParts(type)) { + if (t.getCallSignatures().some(matcher)) { + return true; + } + } + + return false; +} + +function isFunctionParam( + checker: ts.TypeChecker, + param: ts.Symbol, + node: ts.Node, +): boolean { + let type: ts.Type | undefined = checker.getApparentType( + checker.getTypeOfSymbolAtLocation(param, node), + ); + for (const t of tsutils.unionTypeParts(type)) { + if (t.getCallSignatures().length !== 0) { + return true; + } + } + return false; +} + +function isPromiseCatchCallWithHandler(expression: ts.CallExpression): boolean { + return ( + tsutils.isPropertyAccessExpression(expression.expression) && + expression.expression.name.text === 'catch' && + expression.arguments.length >= 1 + ); +} + +function isPromiseThenCallWithRejectionHandler( + expression: ts.CallExpression, +): boolean { + return ( + tsutils.isPropertyAccessExpression(expression.expression) && + expression.expression.name.text === 'then' && + expression.arguments.length >= 2 + ); +} diff --git a/packages/eslint-plugin/tests/rules/no-floating-promises.test.ts b/packages/eslint-plugin/tests/rules/no-floating-promises.test.ts new file mode 100644 index 000000000000..abe2bdf0d7ed --- /dev/null +++ b/packages/eslint-plugin/tests/rules/no-floating-promises.test.ts @@ -0,0 +1,549 @@ +import rule from '../../src/rules/no-floating-promises'; +import { RuleTester, getFixturesRootDir } from '../RuleTester'; + +const rootDir = getFixturesRootDir(); +const parserOptions = { + ecmaVersion: 2018, + tsconfigRootDir: rootDir, + project: './tsconfig.json', +}; + +const messageId = 'floating'; + +const ruleTester = new RuleTester({ + parserOptions, + parser: '@typescript-eslint/parser', +}); + +ruleTester.run('no-floating-promises', rule, { + valid: [ + ` +async function test() { + await Promise.resolve("value"); + Promise.resolve("value").then(() => {}, () => {}); + Promise.resolve("value").then(() => {}).catch(() => {}); + Promise.resolve("value").catch(() => {}); + return Promise.resolve("value"); +} +`, + ` +async function test() { + await Promise.reject(new Error("message")); + Promise.reject(new Error("message")).then(() => {}, () => {}); + Promise.reject(new Error("message")).then(() => {}).catch(() => {}); + Promise.reject(new Error("message")).catch(() => {}); + return Promise.reject(new Error("message")); +} +`, + ` +async function test() { + await (async () => true)(); + (async () => true)().then(() => {}, () => {}); + (async () => true)().then(() => {}).catch(() => {}); + (async () => true)().catch(() => {}); + return (async () => true)(); +} +`, + ` +async function test() { + async function returnsPromise() {} + await returnsPromise(); + returnsPromise().then(() => {}, () => {}); + returnsPromise().then(() => {}).catch(() => {}); + returnsPromise().catch(() => {}); + return returnsPromise(); +} +`, + ` +async function test() { + const x = Promise.resolve(); + const y = x.then(() => {}); + y.catch(() => {}); +} +`, + ` +async function test() { + Math.random() > 0.5 ? Promise.resolve().catch(() => {}) : null; +} +`, + ` +async function test() { + Promise.resolve().catch(() => {}), 123; + 123, Promise.resolve().then(() => {}, () => {}); + 123, Promise.resolve().then(() => {}, () => {}), 123; +} +`, + ` +async function test() { + void Promise.resolve().catch(() => {}); +} +`, + ` +async function test() { + Promise.resolve().catch(() => {}) || Promise.resolve().then(() => {}, () => {}); +} +`, + ` +async function test() { + const promiseValue: Promise; + + await promiseValue; + promiseValue.then(() => {}, () => {}); + promiseValue.then(() => {}).catch(() => {}); + promiseValue.catch(() => {}); + return promiseValue; +} +`, + ` +async function test() { + const promiseUnion: Promise | number; + + await promiseUnion; + promiseUnion.then(() => {}, () => {}); + promiseUnion.then(() => {}).catch(() => {}); + promiseUnion.catch(() => {}); + return promiseUnion; +} +`, + ` +async function test() { + const promiseIntersection: Promise & number; + + await promiseIntersection; + promiseIntersection.then(() => {}, () => {}); + promiseIntersection.then(() => {}).catch(() => {}); + promiseIntersection.catch(() => {}); + return promiseIntersection; +} +`, + ` +async function test() { + class CanThen extends Promise {} + const canThen: CanThen = Foo.resolve(2); + + await canThen; + canThen.then(() => {}, () => {}); + canThen.then(() => {}).catch(() => {}); + canThen.catch(() => {}); + return canThen; +} +`, + ` +async function test() { + await (Math.random() > 0.5 ? numberPromise : 0); + await (Math.random() > 0.5 ? foo : 0); + await (Math.random() > 0.5 ? bar : 0); + + const intersectionPromise: Promise & number; + await intersectionPromise; +} +`, + ` +async function test() { + class Thenable { + then(callback: () => {}): Thenable { return new Thenable(); } + }; + const thenable = new Thenable(); + + await thenable; + thenable; + thenable.then(() => {}); + return thenable; +} +`, + ` +async function test() { + class NonFunctionParamThenable { + then(param: string, param2: number): NonFunctionParamThenable { + return new NonFunctionParamThenable(); + } + }; + const thenable = new NonFunctionParamThenable(); + + await thenable; + thenable; + thenable.then('abc', 'def'); + return thenable; +} +`, + ` +async function test() { + class NonFunctionThenable { then: number }; + const thenable = new NonFunctionThenable(); + + thenable; + thenable.then; + return thenable; +} +`, + ` +async function test() { + class CatchableThenable { + then(callback: () => {}, callback: () => {}): CatchableThenable { + return new CatchableThenable(); + } + }; + const thenable = new CatchableThenable(); + + await thenable + return thenable; +} +`, + ` +// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/promise-polyfill/index.d.ts +// Type definitions for promise-polyfill 6.0 +// Project: https://github.com/taylorhakes/promise-polyfill +// Definitions by: Steve Jenkins +// Daniel Cassidy +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +interface PromisePolyfillConstructor extends PromiseConstructor { + _immediateFn?: (handler: (() => void) | string) => void; +} + +declare const PromisePolyfill: PromisePolyfillConstructor; + +async function test() { + const promise = new PromisePolyfill(() => {}); + + await promise; + promise.then(() => {}, () => {}); + promise.then(() => {}).catch(() => {}); + promise.catch(() => {}); + return promise; +} +`, + ], + + invalid: [ + { + code: ` +async function test() { + Promise.resolve("value"); + Promise.resolve("value").then(() => {}); + Promise.resolve("value").catch(); +} +`, + errors: [ + { + line: 3, + messageId, + }, + { + line: 4, + messageId, + }, + { + line: 5, + messageId, + }, + ], + }, + { + code: ` +async function test() { + Promise.reject(new Error("message")); + Promise.reject(new Error("message")).then(() => {}); + Promise.reject(new Error("message")).catch(); +} +`, + errors: [ + { + line: 3, + messageId, + }, + { + line: 4, + messageId, + }, + { + line: 5, + messageId, + }, + ], + }, + { + code: ` +async function test() { + (async () => true)(); + (async () => true)().then(() => {}); + (async () => true)().catch(); +} +`, + errors: [ + { + line: 3, + messageId, + }, + { + line: 4, + messageId, + }, + { + line: 5, + messageId, + }, + ], + }, + { + code: ` +async function test() { + async function returnsPromise() {} + + returnsPromise(); + returnsPromise().then(() => {}); + returnsPromise().catch(); +} +`, + errors: [ + { + line: 5, + messageId, + }, + { + line: 6, + messageId, + }, + { + line: 7, + messageId, + }, + ], + }, + { + code: ` +async function test() { + Math.random() > 0.5 ? Promise.resolve() : null; + Math.random() > 0.5 ? null : Promise.resolve(); +} +`, + errors: [ + { + line: 3, + messageId, + }, + { + line: 4, + messageId, + }, + ], + }, + { + code: ` +async function test() { + Promise.resolve(), 123 + 123, Promise.resolve() + 123, Promise.resolve(), 123 +} +`, + errors: [ + { + line: 3, + messageId, + }, + { + line: 4, + messageId, + }, + { + line: 5, + messageId, + }, + ], + }, + { + code: ` +async function test() { + void Promise.resolve(); +} +`, + errors: [ + { + line: 3, + messageId, + }, + ], + }, + { + code: ` +async function test() { + const obj = { foo: Promise.resolve() }; + obj.foo; +} +`, + errors: [ + { + line: 4, + messageId, + }, + ], + }, + { + code: ` +async function test() { + new Promise(resolve => resolve()); +} +`, + errors: [ + { + line: 3, + messageId, + }, + ], + }, + { + code: ` +async function test() { + const promiseValue: Promise; + + promiseValue; + promiseValue.then(() => {}); + promiseValue.catch(); +} +`, + errors: [ + { + line: 5, + messageId, + }, + { + line: 6, + messageId, + }, + { + line: 7, + messageId, + }, + ], + }, + { + code: ` +async function test() { + const promiseUnion: Promise | number; + + promiseUnion; +} +`, + errors: [ + { + line: 5, + messageId, + }, + ], + }, + { + code: ` +async function test() { + const promiseIntersection: Promise & number; + + promiseIntersection; + promiseIntersection.then(() => {}) + promiseIntersection.catch(); +} +`, + errors: [ + { + line: 5, + messageId, + }, + { + line: 6, + messageId, + }, + { + line: 7, + messageId, + }, + ], + }, + { + code: ` +async function test() { + class CanThen extends Promise {} + const canThen: CanThen = Foo.resolve(2); + + canThen; + canThen.then(() => {}); + canThen.catch(); +} +`, + errors: [ + { + line: 6, + messageId, + }, + { + line: 7, + messageId, + }, + { + line: 8, + messageId, + }, + ], + }, + { + code: ` +async function test() { + class CatchableThenable { + then(callback: () => {}, callback: () => {}): CatchableThenable { + return new CatchableThenable(); + } + }; + const thenable = new CatchableThenable(); + + thenable; + thenable.then(() => {}); +} +`, + errors: [ + { + line: 10, + messageId, + }, + { + line: 11, + messageId, + }, + ], + }, + { + code: ` +// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/promise-polyfill/index.d.ts +// Type definitions for promise-polyfill 6.0 +// Project: https://github.com/taylorhakes/promise-polyfill +// Definitions by: Steve Jenkins +// Daniel Cassidy +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +interface PromisePolyfillConstructor extends PromiseConstructor { + _immediateFn?: (handler: (() => void) | string) => void; +} + +declare const PromisePolyfill: PromisePolyfillConstructor; + +async function test() { + const promise = new PromisePolyfill(() => {}); + + promise; + promise.then(() => {}); + promise.catch(); +} +`, + errors: [ + { + line: 18, + messageId, + }, + { + line: 19, + messageId, + }, + { + line: 20, + messageId, + }, + ], + }, + ], +}); From 66c847e9b85b10a73421733d298a5f1ca4ad2aac Mon Sep 17 00:00:00 2001 From: Jeff Principe Date: Sun, 9 Jun 2019 12:39:09 -0700 Subject: [PATCH 2/5] chore: updates to fix build/test --- packages/eslint-plugin/src/rules/index.ts | 2 ++ packages/eslint-plugin/src/rules/no-floating-promises.ts | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index d62f58d6af3f..c92f6a36d8b9 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -20,6 +20,7 @@ import noEmptyInterface from './no-empty-interface'; import noExplicitAny from './no-explicit-any'; import noExtraParens from './no-extra-parens'; import noExtraneousClass from './no-extraneous-class'; +import noFloatingPromises from './no-floating-promises'; import noForInArray from './no-for-in-array'; import noInferrableTypes from './no-inferrable-types'; import noMagicNumbers from './no-magic-numbers'; @@ -76,6 +77,7 @@ export default { 'no-explicit-any': noExplicitAny, 'no-extra-parens': noExtraParens, 'no-extraneous-class': noExtraneousClass, + 'no-floating-promises': noFloatingPromises, 'no-for-in-array': noForInArray, 'no-inferrable-types': noInferrableTypes, 'no-magic-numbers': noMagicNumbers, diff --git a/packages/eslint-plugin/src/rules/no-floating-promises.ts b/packages/eslint-plugin/src/rules/no-floating-promises.ts index 2ab6a631d6f0..737c68531d0c 100644 --- a/packages/eslint-plugin/src/rules/no-floating-promises.ts +++ b/packages/eslint-plugin/src/rules/no-floating-promises.ts @@ -11,7 +11,6 @@ export default util.createRule({ 'Requires promises to be awaited or have a rejection handler.', category: 'Best Practices', recommended: false, - tslintName: 'no-floating-promises', }, messages: { floating: 'Promises must be handled appropriately', From fbdbff349e268aaaf31b6e4a886c384ba79b4d4b Mon Sep 17 00:00:00 2001 From: Jeff Principe Date: Sun, 9 Jun 2019 12:49:50 -0700 Subject: [PATCH 3/5] chore: fix typecheck --- .../tests/rules/no-floating-promises.test.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) 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 abe2bdf0d7ed..63a360715d91 100644 --- a/packages/eslint-plugin/tests/rules/no-floating-promises.test.ts +++ b/packages/eslint-plugin/tests/rules/no-floating-promises.test.ts @@ -2,16 +2,14 @@ import rule from '../../src/rules/no-floating-promises'; import { RuleTester, getFixturesRootDir } from '../RuleTester'; const rootDir = getFixturesRootDir(); -const parserOptions = { - ecmaVersion: 2018, - tsconfigRootDir: rootDir, - project: './tsconfig.json', -}; - const messageId = 'floating'; const ruleTester = new RuleTester({ - parserOptions, + parserOptions: { + ecmaVersion: 2018, + tsconfigRootDir: rootDir, + project: './tsconfig.json', + }, parser: '@typescript-eslint/parser', }); From f7601d56e5479a85f3572efe099a7bbecf31a5bd Mon Sep 17 00:00:00 2001 From: Jeff Principe Date: Sun, 9 Jun 2019 13:02:15 -0700 Subject: [PATCH 4/5] chore: fix lint error --- packages/eslint-plugin/src/rules/no-floating-promises.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/no-floating-promises.ts b/packages/eslint-plugin/src/rules/no-floating-promises.ts index 737c68531d0c..968bcb881025 100644 --- a/packages/eslint-plugin/src/rules/no-floating-promises.ts +++ b/packages/eslint-plugin/src/rules/no-floating-promises.ts @@ -142,7 +142,7 @@ function isFunctionParam( param: ts.Symbol, node: ts.Node, ): boolean { - let type: ts.Type | undefined = checker.getApparentType( + const type: ts.Type | undefined = checker.getApparentType( checker.getTypeOfSymbolAtLocation(param, node), ); for (const t of tsutils.unionTypeParts(type)) { From def11d3794d723d2c51cb15ef95606f95ac3171d Mon Sep 17 00:00:00 2001 From: Jeff Principe Date: Sun, 9 Jun 2019 13:13:30 -0700 Subject: [PATCH 5/5] chore: fix docs mismatch --- packages/eslint-plugin/src/rules/no-floating-promises.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-floating-promises.ts b/packages/eslint-plugin/src/rules/no-floating-promises.ts index 968bcb881025..1c8cf412c070 100644 --- a/packages/eslint-plugin/src/rules/no-floating-promises.ts +++ b/packages/eslint-plugin/src/rules/no-floating-promises.ts @@ -7,8 +7,7 @@ export default util.createRule({ name: 'no-floating-promises', meta: { docs: { - description: - 'Requires promises to be awaited or have a rejection handler.', + description: 'Requires Promise-like values to be handled appropriately.', category: 'Best Practices', recommended: false, }, pFad - Phonifier reborn

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

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


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy