Skip to content

Commit 7f8d024

Browse files
Copilotsandersn
andauthored
Fix @Satisfies tag preventing @param types from being applied to arrow functions (#3746)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: sandersn <293473+sandersn@users.noreply.github.com>
1 parent 0d33019 commit 7f8d024

12 files changed

Lines changed: 237 additions & 80 deletions

internal/parser/reparser.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -564,19 +564,27 @@ func findMatchingParameter(fun *ast.Node, parameterTag *ast.JSDocParameterOrProp
564564
return nil, false
565565
}
566566

567+
func skipSatisfiesExpressions(node *ast.Node) *ast.Node {
568+
for node != nil && node.Kind == ast.KindSatisfiesExpression {
569+
node = node.Expression()
570+
}
571+
return node
572+
}
573+
567574
func getFunctionLikeHost(host *ast.Node) *ast.Node {
568575
fun := host
569576
if host.Kind == ast.KindVariableStatement && host.AsVariableStatement().DeclarationList != nil {
570577
for _, declaration := range host.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes {
571-
if ast.IsFunctionLike(declaration.Initializer()) {
572-
fun = declaration.Initializer()
578+
initializer := skipSatisfiesExpressions(declaration.Initializer())
579+
if ast.IsFunctionLike(initializer) {
580+
fun = initializer
573581
break
574582
}
575583
}
576584
} else if host.Kind == ast.KindPropertyAssignment {
577-
fun = host.Initializer()
585+
fun = skipSatisfiesExpressions(host.Initializer())
578586
} else if host.Kind == ast.KindPropertyDeclaration {
579-
fun = host.Initializer()
587+
fun = skipSatisfiesExpressions(host.Initializer())
580588
} else if host.Kind == ast.KindExportAssignment {
581589
fun = host.Expression()
582590
} else if host.Kind == ast.KindReturnStatement {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/a.js(9,16): error TS1360: Type '(a: string, b: string) => void' does not satisfy the expected type '(a: string, ...args: number[]) => void'.
2+
Types of parameters 'b' and 'args' are incompatible.
3+
Type 'number' is not assignable to type 'string'.
4+
5+
6+
==== /a.js (1 errors) ====
7+
/**
8+
* @satisfies {(a: string, ...args: never) => void}
9+
* @param {string} a
10+
* @param {number} b
11+
*/
12+
export const fn1 = (a, b) => {};
13+
14+
/**
15+
* @satisfies {(a: string, ...args: number[]) => void}
16+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17+
!!! error TS1360: Type '(a: string, b: string) => void' does not satisfy the expected type '(a: string, ...args: number[]) => void'.
18+
!!! error TS1360: Types of parameters 'b' and 'args' are incompatible.
19+
!!! error TS1360: Type 'number' is not assignable to type 'string'.
20+
* @param {string} a
21+
* @param {string} b
22+
*/
23+
export const fn2 = (a, b) => {};
24+
25+
/**
26+
* @satisfies {(a: string, ...args: number[]) => void}
27+
* @param {string} a
28+
* @param {string | number} b
29+
*/
30+
export const fn3 = (a, b) => {};
31+
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//// [tests/cases/compiler/jsdocSatisfiesTagWithParamTag.ts] ////
2+
3+
//// [a.js]
4+
/**
5+
* @satisfies {(a: string, ...args: never) => void}
6+
* @param {string} a
7+
* @param {number} b
8+
*/
9+
export const fn1 = (a, b) => {};
10+
11+
/**
12+
* @satisfies {(a: string, ...args: number[]) => void}
13+
* @param {string} a
14+
* @param {string} b
15+
*/
16+
export const fn2 = (a, b) => {};
17+
18+
/**
19+
* @satisfies {(a: string, ...args: number[]) => void}
20+
* @param {string} a
21+
* @param {string | number} b
22+
*/
23+
export const fn3 = (a, b) => {};
24+
25+
26+
//// [a.js]
27+
/**
28+
* @satisfies {(a: string, ...args: never) => void}
29+
* @param {string} a
30+
* @param {number} b
31+
*/
32+
export const fn1 = (a, b) => { };
33+
/**
34+
* @satisfies {(a: string, ...args: number[]) => void}
35+
* @param {string} a
36+
* @param {string} b
37+
*/
38+
export const fn2 = (a, b) => { };
39+
/**
40+
* @satisfies {(a: string, ...args: number[]) => void}
41+
* @param {string} a
42+
* @param {string | number} b
43+
*/
44+
export const fn3 = (a, b) => { };
45+
46+
47+
//// [a.d.ts]
48+
/**
49+
* @satisfies {(a: string, ...args: never) => void}
50+
* @param {string} a
51+
* @param {number} b
52+
*/
53+
export declare const fn1: (a: string, b: number) => void;
54+
/**
55+
* @satisfies {(a: string, ...args: number[]) => void}
56+
* @param {string} a
57+
* @param {string} b
58+
*/
59+
export declare const fn2: (a: string, b: string) => void;
60+
/**
61+
* @satisfies {(a: string, ...args: number[]) => void}
62+
* @param {string} a
63+
* @param {string | number} b
64+
*/
65+
export declare const fn3: (a: string, b: string | number) => void;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//// [tests/cases/compiler/jsdocSatisfiesTagWithParamTag.ts] ////
2+
3+
=== /a.js ===
4+
/**
5+
* @satisfies {(a: string, ...args: never) => void}
6+
* @param {string} a
7+
* @param {number} b
8+
*/
9+
export const fn1 = (a, b) => {};
10+
>fn1 : Symbol(fn1, Decl(a.js, 5, 12))
11+
>a : Symbol(a, Decl(a.js, 5, 20))
12+
>b : Symbol(b, Decl(a.js, 5, 22))
13+
14+
/**
15+
* @satisfies {(a: string, ...args: number[]) => void}
16+
* @param {string} a
17+
* @param {string} b
18+
*/
19+
export const fn2 = (a, b) => {};
20+
>fn2 : Symbol(fn2, Decl(a.js, 12, 12))
21+
>a : Symbol(a, Decl(a.js, 12, 20))
22+
>b : Symbol(b, Decl(a.js, 12, 22))
23+
24+
/**
25+
* @satisfies {(a: string, ...args: number[]) => void}
26+
* @param {string} a
27+
* @param {string | number} b
28+
*/
29+
export const fn3 = (a, b) => {};
30+
>fn3 : Symbol(fn3, Decl(a.js, 19, 12))
31+
>a : Symbol(a, Decl(a.js, 19, 20))
32+
>b : Symbol(b, Decl(a.js, 19, 22))
33+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//// [tests/cases/compiler/jsdocSatisfiesTagWithParamTag.ts] ////
2+
3+
=== /a.js ===
4+
/**
5+
* @satisfies {(a: string, ...args: never) => void}
6+
* @param {string} a
7+
* @param {number} b
8+
*/
9+
export const fn1 = (a, b) => {};
10+
>fn1 : (a: string, b: number) => void
11+
>(a, b) => {} : (a: string, b: number) => void
12+
>a : string
13+
>b : number
14+
15+
/**
16+
* @satisfies {(a: string, ...args: number[]) => void}
17+
* @param {string} a
18+
* @param {string} b
19+
*/
20+
export const fn2 = (a, b) => {};
21+
>fn2 : (a: string, b: string) => void
22+
>(a, b) => {} : (a: string, b: string) => void
23+
>a : string
24+
>b : string
25+
26+
/**
27+
* @satisfies {(a: string, ...args: number[]) => void}
28+
* @param {string} a
29+
* @param {string | number} b
30+
*/
31+
export const fn3 = (a, b) => {};
32+
>fn3 : (a: string, b: string | number) => void
33+
>(a, b) => {} : (a: string, b: string | number) => void
34+
>a : string
35+
>b : string | number
36+

testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag15.errors.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
/a.js(9,20): error TS2322: Type 'number' is not assignable to type 'string'.
2+
/a.js(28,16): error TS1360: Type '(a: string, b: string) => void' does not satisfy the expected type '(a: string, ...args: number[]) => void'.
3+
Types of parameters 'b' and 'args' are incompatible.
4+
Type 'number' is not assignable to type 'string'.
25
/a.js(42,21): error TS7006: Parameter 'uuid' implicitly has an 'any' type.
36

47

5-
==== /a.js (2 errors) ====
8+
==== /a.js (3 errors) ====
69
/** @satisfies {(uuid: string) => void} */
710
export const fn1 = uuid => {};
811

@@ -33,6 +36,10 @@
3336

3437
/**
3538
* @satisfies {(a: string, ...args: number[]) => void}
39+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
40+
!!! error TS1360: Type '(a: string, b: string) => void' does not satisfy the expected type '(a: string, ...args: number[]) => void'.
41+
!!! error TS1360: Types of parameters 'b' and 'args' are incompatible.
42+
!!! error TS1360: Type 'number' is not assignable to type 'string'.
3643
* @param {string} a
3744
* @param {string} b
3845
*/

testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag15.errors.txt.diff

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,16 @@
33
@@= skipped -0, +0 lines =@@
44
/a.js(9,20): error TS2322: Type 'number' is not assignable to type 'string'.
55
-/a.js(28,5): error TS1360: Type '(a: string, b: string) => void' does not satisfy the expected type '(a: string, ...args: number[]) => void'.
6-
- Types of parameters 'b' and 'args' are incompatible.
7-
- Type 'number' is not assignable to type 'string'.
6+
+/a.js(28,16): error TS1360: Type '(a: string, b: string) => void' does not satisfy the expected type '(a: string, ...args: number[]) => void'.
7+
Types of parameters 'b' and 'args' are incompatible.
8+
Type 'number' is not assignable to type 'string'.
89
/a.js(42,21): error TS7006: Parameter 'uuid' implicitly has an 'any' type.
9-
10-
11-
-==== /a.js (3 errors) ====
12-
+==== /a.js (2 errors) ====
13-
/** @satisfies {(uuid: string) => void} */
14-
export const fn1 = uuid => {};
15-
16-
@@= skipped -35, +32 lines =@@
10+
@@= skipped -35, +35 lines =@@
1711

1812
/**
1913
* @satisfies {(a: string, ...args: number[]) => void}
2014
- ~~~~~~~~~
21-
-!!! error TS1360: Type '(a: string, b: string) => void' does not satisfy the expected type '(a: string, ...args: number[]) => void'.
22-
-!!! error TS1360: Types of parameters 'b' and 'args' are incompatible.
23-
-!!! error TS1360: Type 'number' is not assignable to type 'string'.
24-
* @param {string} a
25-
* @param {string} b
26-
*/
15+
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16+
!!! error TS1360: Type '(a: string, b: string) => void' does not satisfy the expected type '(a: string, ...args: number[]) => void'.
17+
!!! error TS1360: Types of parameters 'b' and 'args' are incompatible.
18+
!!! error TS1360: Type 'number' is not assignable to type 'string'.

testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag15.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,18 +103,18 @@ export declare const fn3: (a: string, b: never) => void;
103103
* @param {string} a
104104
* @param {number} b
105105
*/
106-
export declare const fn4: (a: string, b: never) => void;
106+
export declare const fn4: (a: string, b: number) => void;
107107
/**
108108
* @satisfies {(a: string, ...args: number[]) => void}
109109
* @param {string} a
110110
* @param {string} b
111111
*/
112-
export declare const fn5: (a: string, b: number) => void;
112+
export declare const fn5: (a: string, b: string) => void;
113113
/**
114114
* @satisfies {(a: string, ...args: number[]) => void}
115115
* @param {string} a
116116
* @param {string | number} b
117117
*/
118-
export declare const fn6: (a: string, b: number) => void;
118+
export declare const fn6: (a: string, b: string | number) => void;
119119
/** @satisfies {(uuid: string) => void} */
120120
export declare function fn7(uuid: any): void;

testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag15.types

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,32 +44,32 @@ export const fn3 = (a, b) => {};
4444
* @param {number} b
4545
*/
4646
export const fn4 = (a, b) => {};
47-
>fn4 : (a: string, b: never) => void
48-
>(a, b) => {} : (a: string, b: never) => void
47+
>fn4 : (a: string, b: number) => void
48+
>(a, b) => {} : (a: string, b: number) => void
4949
>a : string
50-
>b : never
50+
>b : number
5151

5252
/**
5353
* @satisfies {(a: string, ...args: number[]) => void}
5454
* @param {string} a
5555
* @param {string} b
5656
*/
5757
export const fn5 = (a, b) => {};
58-
>fn5 : (a: string, b: number) => void
59-
>(a, b) => {} : (a: string, b: number) => void
58+
>fn5 : (a: string, b: string) => void
59+
>(a, b) => {} : (a: string, b: string) => void
6060
>a : string
61-
>b : number
61+
>b : string
6262

6363
/**
6464
* @satisfies {(a: string, ...args: number[]) => void}
6565
* @param {string} a
6666
* @param {string | number} b
6767
*/
6868
export const fn6 = (a, b) => {};
69-
>fn6 : (a: string, b: number) => void
70-
>(a, b) => {} : (a: string, b: number) => void
69+
>fn6 : (a: string, b: string | number) => void
70+
>(a, b) => {} : (a: string, b: string | number) => void
7171
>a : string
72-
>b : number
72+
>b : string | number
7373

7474
/** @satisfies {(uuid: string) => void} */
7575
export function fn7(uuid) {}

testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag15.types.diff

Lines changed: 0 additions & 44 deletions
This file was deleted.

0 commit comments

Comments
 (0)