Repository: eslint-stylistic/eslint-stylistic
Pull request #836 (2025-06-18 16:57:52)

fix(type-annotation-spacing): handle type annotation in parens
<!-- DO NOT IGNORE THE TEMPLATE!

Thank you for contributing!

Before submitting the PR, please make sure you do the following:

- Read the [Contributing Guide](https://eslint.style/contribute/guide).
- Check that there isn't already a PR that solves the problem the same way to avoid creating a duplicate.
- Provide a description in this PR that addresses **what** the PR is solving, or reference the issue that it solves (e.g. `fixes #123`).
- Ideally, include relevant tests that fail without this PR but pass with it.

-->

### Description

<!-- Please insert your description here and provide especially info about the "what" this PR is solving -->

### Linked Issues

Resolve #441 

### Additional context

<!-- e.g. is there anything you'd like reviewers to focus on? -->


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/eslint-stylistic/eslint-stylistic/pull/836?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=eslint-stylistic) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 99.68%. Comparing base [(`7240084`)](https://app.codecov.io/gh/eslint-stylistic/eslint-stylistic/commit/72400840f2604e0364047cd600251986863d4d09?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=eslint-stylistic) to head [(`1a4bfc7`)](https://app.codecov.io/gh/eslint-stylistic/eslint-stylistic/commit/1a4bfc78779b25d565c2b82f72a2397149293eed?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=eslint-stylistic).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #836      +/-   ##
==========================================
- Coverage   99.68%   99.68%   -0.01%     
==========================================
  Files          95       95              
  Lines       18093    18090       -3     
  Branches     4672     4672              
==========================================
- Hits        18036    18033       -3     
  Misses         57       57              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/eslint-stylistic/eslint-stylistic/pull/836?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=eslint-stylistic).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=eslint-stylistic).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>

Patch:
diff --git a/packages/eslint-plugin/rules/space-before-function-paren/README.md b/packages/eslint-plugin/rules/space-before-function-paren/README.md
index bcb0e5ae..7c08e090 100644
--- a/packages/eslint-plugin/rules/space-before-function-paren/README.md
+++ b/packages/eslint-plugin/rules/space-before-function-paren/README.md
@@ -49,8 +49,6 @@ This rule has a string option or an object option:
 - `always` (default) requires a space followed by the `(` of arguments.
 - `never` disallows any space followed by the `(` of arguments.
 
-The string option does not check async arrow function expressions for backward compatibility.
-
 You can also use a separate option for each type of function.
 Each of the following options can be set to `"always"`, `"never"`, or `"ignore"`. The default is `"always"`.
 
diff --git a/packages/eslint-plugin/rules/space-before-function-paren/space-before-function-paren.ts b/packages/eslint-plugin/rules/space-before-function-paren/space-before-function-paren.ts
index b3c8d0ea..d39f4b48 100644
--- a/packages/eslint-plugin/rules/space-before-function-paren/space-before-function-paren.ts
+++ b/packages/eslint-plugin/rules/space-before-function-paren/space-before-function-paren.ts
@@ -110,9 +110,8 @@ export default createRule<RuleOptions, MessageIds>({
       }
       else if (isNamedFunction(node)) {
         return overrideConfig.named ?? baseConfig
-
-        // `generator-star-spacing` should warn anonymous generators. E.g. `function* () {}`
       }
+      // `generator-star-spacing` should warn anonymous generators. E.g. `function* () {}`
       else if (!node.generator) {
         return overrideConfig.anonymous ?? baseConfig
       }
diff --git a/packages/eslint-plugin/rules/type-annotation-spacing/type-annotation-spacing.ts b/packages/eslint-plugin/rules/type-annotation-spacing/type-annotation-spacing.ts
index d66e41d0..59aa1ac8 100644
--- a/packages/eslint-plugin/rules/type-annotation-spacing/type-annotation-spacing.ts
+++ b/packages/eslint-plugin/rules/type-annotation-spacing/type-annotation-spacing.ts
@@ -1,4 +1,5 @@
 import type { ASTNode, Tree } from '#types'
+import { isNotOpeningParenToken } from '#utils/ast'
 import { createRule } from '#utils/create-rule'
 
 import {
@@ -165,8 +166,9 @@ export default createRule<Options, MessageIds>({
     function checkTypeAnnotationSpacing(
       typeAnnotation: Tree.TypeNode,
     ): void {
-      const nextNode = typeAnnotation
-      const punctuatorTokenEnd = sourceCode.getTokenBefore(nextNode)!
+      /** :, => */
+      const punctuatorTokenEnd = sourceCode.getTokenBefore(typeAnnotation, isNotOpeningParenToken)!
+      /** :, =>, ?() */
       let punctuatorTokenStart = punctuatorTokenEnd
       let previousToken = sourceCode.getTokenBefore(punctuatorTokenEnd)!
       let type = punctuatorTokenEnd.value
@@ -177,9 +179,8 @@ export default createRule<Options, MessageIds>({
       const { before, after } = getRules(ruleSet, typeAnnotation)
 
       if (type === ':' && previousToken.value === '?') {
-        if (
-          sourceCode.isSpaceBetween(previousToken, punctuatorTokenStart)
-        ) {
+        // space between ? and :
+        if (sourceCode.isSpaceBetween(previousToken, punctuatorTokenStart)) {
           context.report({
             node: punctuatorTokenStart,
             messageId: 'unexpectedSpaceBetween',
@@ -209,11 +210,9 @@ export default createRule<Options, MessageIds>({
         }
       }
 
-      const previousDelta
-        = punctuatorTokenStart.range[0] - previousToken.range[1]
-      const nextDelta = nextNode.range[0] - punctuatorTokenEnd.range[1]
+      const hasNextSpace = sourceCode.isSpaceBetween(punctuatorTokenEnd, typeAnnotation)
 
-      if (after && nextDelta === 0) {
+      if (after && !hasNextSpace) {
         context.report({
           node: punctuatorTokenEnd,
           messageId: 'expectedSpaceAfter',
@@ -225,7 +224,7 @@ export default createRule<Options, MessageIds>({
           },
         })
       }
-      else if (!after && nextDelta > 0) {
+      else if (!after && hasNextSpace) {
         context.report({
           node: punctuatorTokenEnd,
           messageId: 'unexpectedSpaceAfter',
@@ -235,13 +234,15 @@ export default createRule<Options, MessageIds>({
           fix(fixer) {
             return fixer.removeRange([
               punctuatorTokenEnd.range[1],
-              nextNode.range[0],
+              typeAnnotation.range[0],
             ])
           },
         })
       }
 
-      if (before && previousDelta === 0) {
+      const hasPrevSpace = sourceCode.isSpaceBetween(previousToken, punctuatorTokenStart)
+
+      if (before && !hasPrevSpace) {
         context.report({
           node: punctuatorTokenStart,
           messageId: 'expectedSpaceBefore',
@@ -253,7 +254,7 @@ export default createRule<Options, MessageIds>({
           },
         })
       }
-      else if (!before && previousDelta > 0) {
+      else if (!before && hasPrevSpace) {
         context.report({
           node: punctuatorTokenStart,
           messageId: 'unexpectedSpaceBefore',


Test patch:
diff --git a/packages/eslint-plugin/rules/type-annotation-spacing/type-annotation-spacing.test.ts b/packages/eslint-plugin/rules/type-annotation-spacing/type-annotation-spacing.test.ts
index 759ac7cd..26d1f178 100644
--- a/packages/eslint-plugin/rules/type-annotation-spacing/type-annotation-spacing.test.ts
+++ b/packages/eslint-plugin/rules/type-annotation-spacing/type-annotation-spacing.test.ts
@@ -1171,6 +1171,8 @@ interface Foo {
     },
     // https://github.com/typescript-eslint/typescript-eslint/issues/1663
     'type ConstructorFn = new (...args: any[]) => any;',
+    // https://github.com/eslint-stylistic/eslint-stylistic/issues/441
+    `function error_is_reported_ok(): (void) {}`,
   ],
   invalid: [
     {
@@ -3786,6 +3788,13 @@ type Foo = {
         },
       ],
     },
+    {
+      code: `function error_is_reported_ok():(void) {}`,
+      output: `function error_is_reported_ok(): (void) {}`,
+      errors: [
+        { messageId: 'expectedSpaceAfter', data: { type: ':' }, line: 1, column: 32 },
+      ],
+    },
   ],
 })