Skip to content

⬅️ Back to Table of Contents

📄 promise-function-async.ts

📊 Analysis Summary

Metric Count
🔧 Functions 5
📦 Imports 10
📊 Variables & Constants 5
📑 Type Aliases 2

📚 Table of Contents

🛠️ File Location:

📂 packages/eslint-plugin/src/rules/promise-function-async.ts

📦 Imports

Name Source
TSESTree @typescript-eslint/utils
AST_NODE_TYPES @typescript-eslint/utils
AST_TOKEN_TYPES @typescript-eslint/utils
containsAllTypesByName ../util
createRule ../util
getFunctionHeadLoc ../util
getParserServices ../util
isTypeFlagSet ../util
nullThrows ../util
NullThrowsReasons ../util

Variables & Constants

Name Type Kind Value Exported
allAllowedPromiseNames Set<any> const `new Set([
'Promise',
// https://github.com/typescript-eslint/typescript-eslint/issues/5439
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
...allowedPromiseNames!,
])`
method any const node.parent
lastDecorator any const method.decorators[method.decorators.length - 1]
insertSpace boolean const `!context.sourceCode.isSpaceBetween(
nullThrows(
context.sourceCode.getTokenBefore(keyToken),
NullThrowsReasons.MissingToken('token', 'keyword'),
),
keyToken,
)`
code string let/var 'async '

Functions

`validateNode(node: | TSESTree.ArrowFunctionExpression

    | TSESTree.FunctionDeclaration
    | TSESTree.FunctionExpression): void`
Code
function validateNode(
      node:
        | TSESTree.ArrowFunctionExpression
        | TSESTree.FunctionDeclaration
        | TSESTree.FunctionExpression,
    ): void {
      if (node.parent.type === AST_NODE_TYPES.TSAbstractMethodDefinition) {
        // Abstract method can't be async
        return;
      }

      if (
        (node.parent.type === AST_NODE_TYPES.Property ||
          node.parent.type === AST_NODE_TYPES.MethodDefinition) &&
        (node.parent.kind === 'get' || node.parent.kind === 'set')
      ) {
        // Getters and setters can't be async
        return;
      }

      const signatures = services.getTypeAtLocation(node).getCallSignatures();
      if (!signatures.length) {
        return;
      }

      const returnTypes = signatures.map(signature =>
        checker.getReturnTypeOfSignature(signature),
      );

      if (
        !allowAny &&
        returnTypes.some(type =>
          isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown),
        )
      ) {
        // Report without auto fixer because the return type is unknown
        return context.report({
          loc: getFunctionHeadLoc(node, context.sourceCode),
          node,
          messageId: 'missingAsync',
        });
      }

      if (
        // require all potential return types to be promise/any/unknown
        returnTypes.every(type =>
          containsAllTypesByName(
            type,
            true,
            allAllowedPromiseNames,
            // If no return type is explicitly set, we check if any parts of the return type match a Promise (instead of requiring all to match).
            node.returnType == null,
          ),
        )
      ) {
        const isHybridReturnType = returnTypes.some(
          type =>
            type.isUnion() &&
            !type.types.every(part =>
              containsAllTypesByName(part, true, allAllowedPromiseNames),
            ),
        );

        context.report({
          loc: getFunctionHeadLoc(node, context.sourceCode),
          node,
          messageId: isHybridReturnType
            ? 'missingAsyncHybridReturn'
            : 'missingAsync',
          fix: fixer => {
            if (
              node.parent.type === AST_NODE_TYPES.MethodDefinition ||
              (node.parent.type === AST_NODE_TYPES.Property &&
                node.parent.method)
            ) {
              // this function is a class method or object function property shorthand
              const method = node.parent;

              // the token to put `async` before
              let keyToken = nullThrows(
                context.sourceCode.getFirstToken(method),
                NullThrowsReasons.MissingToken('key token', 'method'),
              );

              // if there are decorators then skip past them
              if (
                method.type === AST_NODE_TYPES.MethodDefinition &&
                method.decorators.length
              ) {
                const lastDecorator =
                  method.decorators[method.decorators.length - 1];
                keyToken = nullThrows(
                  context.sourceCode.getTokenAfter(lastDecorator),
                  NullThrowsReasons.MissingToken('key token', 'last decorator'),
                );
              }

              // if current token is a keyword like `static` or `public` then skip it
              while (
                keyToken.type === AST_TOKEN_TYPES.Keyword &&
                keyToken.range[0] < method.key.range[0]
              ) {
                keyToken = nullThrows(
                  context.sourceCode.getTokenAfter(keyToken),
                  NullThrowsReasons.MissingToken('token', 'keyword'),
                );
              }

              // check if there is a space between key and previous token
              const insertSpace = !context.sourceCode.isSpaceBetween(
                nullThrows(
                  context.sourceCode.getTokenBefore(keyToken),
                  NullThrowsReasons.MissingToken('token', 'keyword'),
                ),
                keyToken,
              );

              let code = 'async ';
              if (insertSpace) {
                code = ` ${code}`;
              }
              return fixer.insertTextBefore(keyToken, code);
            }

            return fixer.insertTextBefore(node, 'async ');
          },
        });
      }
    }
  • Parameters:
  • node: | TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclaration | TSESTree.FunctionExpression
  • Return Type: void
  • Calls:
  • services.getTypeAtLocation(node).getCallSignatures
  • signatures.map
  • checker.getReturnTypeOfSignature
  • returnTypes.some
  • isTypeFlagSet (from ../util)
  • context.report
  • getFunctionHeadLoc (from ../util)
  • returnTypes.every
  • containsAllTypesByName (from ../util)
  • type.isUnion
  • type.types.every
  • nullThrows (from ../util)
  • context.sourceCode.getFirstToken
  • NullThrowsReasons.MissingToken
  • context.sourceCode.getTokenAfter
  • context.sourceCode.isSpaceBetween
  • context.sourceCode.getTokenBefore
  • fixer.insertTextBefore
  • Internal Comments:
    // Abstract method can't be async
    // Getters and setters can't be async
    // Report without auto fixer because the return type is unknown
    // require all potential return types to be promise/any/unknown (x3)
    // If no return type is explicitly set, we check if any parts of the return type match a Promise (instead of requiring all to match). (x3)
    // this function is a class method or object function property shorthand (x2)
    // the token to put `async` before (x2)
    // if there are decorators then skip past them
    // if current token is a keyword like `static` or `public` then skip it
    // check if there is a space between key and previous token (x2)
    

fix(fixer: any): any

Code
fixer => {
            if (
              node.parent.type === AST_NODE_TYPES.MethodDefinition ||
              (node.parent.type === AST_NODE_TYPES.Property &&
                node.parent.method)
            ) {
              // this function is a class method or object function property shorthand
              const method = node.parent;

              // the token to put `async` before
              let keyToken = nullThrows(
                context.sourceCode.getFirstToken(method),
                NullThrowsReasons.MissingToken('key token', 'method'),
              );

              // if there are decorators then skip past them
              if (
                method.type === AST_NODE_TYPES.MethodDefinition &&
                method.decorators.length
              ) {
                const lastDecorator =
                  method.decorators[method.decorators.length - 1];
                keyToken = nullThrows(
                  context.sourceCode.getTokenAfter(lastDecorator),
                  NullThrowsReasons.MissingToken('key token', 'last decorator'),
                );
              }

              // if current token is a keyword like `static` or `public` then skip it
              while (
                keyToken.type === AST_TOKEN_TYPES.Keyword &&
                keyToken.range[0] < method.key.range[0]
              ) {
                keyToken = nullThrows(
                  context.sourceCode.getTokenAfter(keyToken),
                  NullThrowsReasons.MissingToken('token', 'keyword'),
                );
              }

              // check if there is a space between key and previous token
              const insertSpace = !context.sourceCode.isSpaceBetween(
                nullThrows(
                  context.sourceCode.getTokenBefore(keyToken),
                  NullThrowsReasons.MissingToken('token', 'keyword'),
                ),
                keyToken,
              );

              let code = 'async ';
              if (insertSpace) {
                code = ` ${code}`;
              }
              return fixer.insertTextBefore(keyToken, code);
            }

            return fixer.insertTextBefore(node, 'async ');
          }
  • Parameters:
  • fixer: any
  • Return Type: any
  • Calls:
  • nullThrows (from ../util)
  • context.sourceCode.getFirstToken
  • NullThrowsReasons.MissingToken
  • context.sourceCode.getTokenAfter
  • context.sourceCode.isSpaceBetween
  • context.sourceCode.getTokenBefore
  • fixer.insertTextBefore
  • Internal Comments:
    // this function is a class method or object function property shorthand (x2)
    // the token to put `async` before (x2)
    // if there are decorators then skip past them
    // if current token is a keyword like `static` or `public` then skip it
    // check if there is a space between key and previous token (x2)
    

fix(fixer: any): any

Code
fixer => {
            if (
              node.parent.type === AST_NODE_TYPES.MethodDefinition ||
              (node.parent.type === AST_NODE_TYPES.Property &&
                node.parent.method)
            ) {
              // this function is a class method or object function property shorthand
              const method = node.parent;

              // the token to put `async` before
              let keyToken = nullThrows(
                context.sourceCode.getFirstToken(method),
                NullThrowsReasons.MissingToken('key token', 'method'),
              );

              // if there are decorators then skip past them
              if (
                method.type === AST_NODE_TYPES.MethodDefinition &&
                method.decorators.length
              ) {
                const lastDecorator =
                  method.decorators[method.decorators.length - 1];
                keyToken = nullThrows(
                  context.sourceCode.getTokenAfter(lastDecorator),
                  NullThrowsReasons.MissingToken('key token', 'last decorator'),
                );
              }

              // if current token is a keyword like `static` or `public` then skip it
              while (
                keyToken.type === AST_TOKEN_TYPES.Keyword &&
                keyToken.range[0] < method.key.range[0]
              ) {
                keyToken = nullThrows(
                  context.sourceCode.getTokenAfter(keyToken),
                  NullThrowsReasons.MissingToken('token', 'keyword'),
                );
              }

              // check if there is a space between key and previous token
              const insertSpace = !context.sourceCode.isSpaceBetween(
                nullThrows(
                  context.sourceCode.getTokenBefore(keyToken),
                  NullThrowsReasons.MissingToken('token', 'keyword'),
                ),
                keyToken,
              );

              let code = 'async ';
              if (insertSpace) {
                code = ` ${code}`;
              }
              return fixer.insertTextBefore(keyToken, code);
            }

            return fixer.insertTextBefore(node, 'async ');
          }
  • Parameters:
  • fixer: any
  • Return Type: any
  • Calls:
  • nullThrows (from ../util)
  • context.sourceCode.getFirstToken
  • NullThrowsReasons.MissingToken
  • context.sourceCode.getTokenAfter
  • context.sourceCode.isSpaceBetween
  • context.sourceCode.getTokenBefore
  • fixer.insertTextBefore
  • Internal Comments:
    // this function is a class method or object function property shorthand (x2)
    // the token to put `async` before (x2)
    // if there are decorators then skip past them
    // if current token is a keyword like `static` or `public` then skip it
    // check if there is a space between key and previous token (x2)
    

fix(fixer: any): any

Code
fixer => {
            if (
              node.parent.type === AST_NODE_TYPES.MethodDefinition ||
              (node.parent.type === AST_NODE_TYPES.Property &&
                node.parent.method)
            ) {
              // this function is a class method or object function property shorthand
              const method = node.parent;

              // the token to put `async` before
              let keyToken = nullThrows(
                context.sourceCode.getFirstToken(method),
                NullThrowsReasons.MissingToken('key token', 'method'),
              );

              // if there are decorators then skip past them
              if (
                method.type === AST_NODE_TYPES.MethodDefinition &&
                method.decorators.length
              ) {
                const lastDecorator =
                  method.decorators[method.decorators.length - 1];
                keyToken = nullThrows(
                  context.sourceCode.getTokenAfter(lastDecorator),
                  NullThrowsReasons.MissingToken('key token', 'last decorator'),
                );
              }

              // if current token is a keyword like `static` or `public` then skip it
              while (
                keyToken.type === AST_TOKEN_TYPES.Keyword &&
                keyToken.range[0] < method.key.range[0]
              ) {
                keyToken = nullThrows(
                  context.sourceCode.getTokenAfter(keyToken),
                  NullThrowsReasons.MissingToken('token', 'keyword'),
                );
              }

              // check if there is a space between key and previous token
              const insertSpace = !context.sourceCode.isSpaceBetween(
                nullThrows(
                  context.sourceCode.getTokenBefore(keyToken),
                  NullThrowsReasons.MissingToken('token', 'keyword'),
                ),
                keyToken,
              );

              let code = 'async ';
              if (insertSpace) {
                code = ` ${code}`;
              }
              return fixer.insertTextBefore(keyToken, code);
            }

            return fixer.insertTextBefore(node, 'async ');
          }
  • Parameters:
  • fixer: any
  • Return Type: any
  • Calls:
  • nullThrows (from ../util)
  • context.sourceCode.getFirstToken
  • NullThrowsReasons.MissingToken
  • context.sourceCode.getTokenAfter
  • context.sourceCode.isSpaceBetween
  • context.sourceCode.getTokenBefore
  • fixer.insertTextBefore
  • Internal Comments:
    // this function is a class method or object function property shorthand (x2)
    // the token to put `async` before (x2)
    // if there are decorators then skip past them
    // if current token is a keyword like `static` or `public` then skip it
    // check if there is a space between key and previous token (x2)
    

fix(fixer: any): any

Code
fixer => {
            if (
              node.parent.type === AST_NODE_TYPES.MethodDefinition ||
              (node.parent.type === AST_NODE_TYPES.Property &&
                node.parent.method)
            ) {
              // this function is a class method or object function property shorthand
              const method = node.parent;

              // the token to put `async` before
              let keyToken = nullThrows(
                context.sourceCode.getFirstToken(method),
                NullThrowsReasons.MissingToken('key token', 'method'),
              );

              // if there are decorators then skip past them
              if (
                method.type === AST_NODE_TYPES.MethodDefinition &&
                method.decorators.length
              ) {
                const lastDecorator =
                  method.decorators[method.decorators.length - 1];
                keyToken = nullThrows(
                  context.sourceCode.getTokenAfter(lastDecorator),
                  NullThrowsReasons.MissingToken('key token', 'last decorator'),
                );
              }

              // if current token is a keyword like `static` or `public` then skip it
              while (
                keyToken.type === AST_TOKEN_TYPES.Keyword &&
                keyToken.range[0] < method.key.range[0]
              ) {
                keyToken = nullThrows(
                  context.sourceCode.getTokenAfter(keyToken),
                  NullThrowsReasons.MissingToken('token', 'keyword'),
                );
              }

              // check if there is a space between key and previous token
              const insertSpace = !context.sourceCode.isSpaceBetween(
                nullThrows(
                  context.sourceCode.getTokenBefore(keyToken),
                  NullThrowsReasons.MissingToken('token', 'keyword'),
                ),
                keyToken,
              );

              let code = 'async ';
              if (insertSpace) {
                code = ` ${code}`;
              }
              return fixer.insertTextBefore(keyToken, code);
            }

            return fixer.insertTextBefore(node, 'async ');
          }
  • Parameters:
  • fixer: any
  • Return Type: any
  • Calls:
  • nullThrows (from ../util)
  • context.sourceCode.getFirstToken
  • NullThrowsReasons.MissingToken
  • context.sourceCode.getTokenAfter
  • context.sourceCode.isSpaceBetween
  • context.sourceCode.getTokenBefore
  • fixer.insertTextBefore
  • Internal Comments:
    // this function is a class method or object function property shorthand (x2)
    // the token to put `async` before (x2)
    // if there are decorators then skip past them
    // if current token is a keyword like `static` or `public` then skip it
    // check if there is a space between key and previous token (x2)
    

Type Aliases

Options

type Options = [
  {
    allowAny?: boolean;
    allowedPromiseNames?: string[];
    checkArrowFunctions?: boolean;
    checkFunctionDeclarations?: boolean;
    checkFunctionExpressions?: boolean;
    checkMethodDeclarations?: boolean;
  },
];

MessageIds

type MessageIds = 'missingAsync' | 'missingAsyncHybridReturn';