Skip to content

Commit 8975084

Browse files
authored
fix(1196): extend parameter matching with index fallback when name matching fails (microsoft#1241)
1 parent b6f391a commit 8975084

File tree

68 files changed

+525
-983
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+525
-983
lines changed

internal/parser/reparser.go

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func (p *Parser) reparseUnhosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Nod
7474
case ast.KindJSDocTypeExpression:
7575
t = setHost(typeExpression, typeAlias)
7676
case ast.KindJSDocTypeLiteral:
77-
t = p.reparseJSDocTypeLiteral(typeExpression, typeAlias)
77+
t = p.reparseJSDocTypeLiteral(typeExpression)
7878
default:
7979
panic("typedef tag type expression should be a name reference or a type expression" + typeExpression.Kind.String())
8080
}
@@ -148,9 +148,7 @@ func (p *Parser) reparseJSDocSignature(jsSignature *ast.Node, fun *ast.Node, jsD
148148
jsparam := param.AsJSDocParameterOrPropertyTag()
149149
parameter = p.factory.NewParameterDeclaration(nil, nil, jsparam.Name(), p.makeQuestionIfOptional(jsparam), nil, nil)
150150
if jsparam.TypeExpression != nil {
151-
t := p.reparseJSDocTypeLiteral(jsparam.TypeExpression.Type(), parameter)
152-
setHost(jsparam.TypeExpression, parameter)
153-
parameter.AsParameterDeclaration().Type = t
151+
parameter.AsParameterDeclaration().Type = p.reparseJSDocTypeLiteral(setHost(jsparam.TypeExpression, parameter))
154152
}
155153
}
156154
parameter.Loc = param.Loc
@@ -170,8 +168,12 @@ func (p *Parser) reparseJSDocSignature(jsSignature *ast.Node, fun *ast.Node, jsD
170168
return signature
171169
}
172170

173-
func (p *Parser) reparseJSDocTypeLiteral(t *ast.TypeNode, host *ast.Node) *ast.Node {
174-
if t != nil && t.Kind == ast.KindJSDocTypeLiteral {
171+
func (p *Parser) reparseJSDocTypeLiteral(t *ast.TypeNode) *ast.Node {
172+
if t == nil {
173+
return nil
174+
}
175+
if t.Kind == ast.KindJSDocTypeLiteral {
176+
isArrayType := t.AsJSDocTypeLiteral().IsArrayType
175177
properties := p.nodeSlicePool.NewSlice(0)
176178
for _, prop := range t.AsJSDocTypeLiteral().JSDocPropertyTags {
177179
jsprop := prop.AsJSDocParameterOrPropertyTag()
@@ -180,7 +182,9 @@ func (p *Parser) reparseJSDocTypeLiteral(t *ast.TypeNode, host *ast.Node) *ast.N
180182
name = name.AsQualifiedName().Right
181183
}
182184
property := p.factory.NewPropertySignatureDeclaration(nil, name, p.makeQuestionIfOptional(jsprop), nil, nil)
183-
property.AsPropertySignatureDeclaration().Type = p.reparseJSDocTypeLiteral(jsprop.TypeExpression, property)
185+
if jsprop.TypeExpression != nil {
186+
property.AsPropertySignatureDeclaration().Type = p.reparseJSDocTypeLiteral(jsprop.TypeExpression.Type())
187+
}
184188
property.Loc = prop.Loc
185189
property.Flags = p.contextFlags | ast.NodeFlagsReparsed
186190
properties = append(properties, property)
@@ -189,6 +193,11 @@ func (p *Parser) reparseJSDocTypeLiteral(t *ast.TypeNode, host *ast.Node) *ast.N
189193
t = p.factory.NewTypeLiteralNode(p.newNodeList(loc, properties))
190194
t.Loc = loc
191195
t.Flags = p.contextFlags | ast.NodeFlagsReparsed
196+
if isArrayType {
197+
t = p.factory.NewArrayTypeNode(t)
198+
t.Flags = p.contextFlags | ast.NodeFlagsReparsed
199+
t.Loc = loc
200+
}
192201
}
193202
return t
194203
}
@@ -327,13 +336,13 @@ func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node)
327336
}
328337
case ast.KindJSDocParameterTag:
329338
if fun, ok := getFunctionLikeHost(parent); ok {
330-
jsparam := tag.AsJSDocParameterOrPropertyTag()
331-
if param, ok := findMatchingParameter(fun, jsparam); ok {
339+
parameterTag := tag.AsJSDocParameterOrPropertyTag()
340+
if param, ok := findMatchingParameter(fun, parameterTag, jsDoc); ok {
332341
if param.Type == nil {
333-
param.Type = setHost(jsparam.TypeExpression, param.AsNode())
342+
param.AsParameterDeclaration().Type = p.reparseJSDocTypeLiteral(setHost(p.reparseJSDocTypeLiteral(parameterTag.TypeExpression), param.AsNode()))
334343
}
335344
if param.QuestionToken == nil && param.Initializer == nil {
336-
if question := p.makeQuestionIfOptional(jsparam); question != nil {
345+
if question := p.makeQuestionIfOptional(parameterTag); question != nil {
337346
param.QuestionToken = question
338347
}
339348
}
@@ -461,11 +470,19 @@ func (p *Parser) makeQuestionIfOptional(parameter *ast.JSDocParameterTag) *ast.N
461470
return questionToken
462471
}
463472

464-
func findMatchingParameter(fun *ast.Node, tag *ast.JSDocParameterTag) (*ast.ParameterDeclaration, bool) {
465-
for _, parameter := range fun.Parameters() {
466-
if parameter.Name().Kind == ast.KindIdentifier && tag.Name().Kind == ast.KindIdentifier &&
467-
parameter.Name().Text() == tag.Name().Text() {
468-
return parameter.AsParameterDeclaration(), true
473+
func findMatchingParameter(fun *ast.Node, tag *ast.JSDocParameterTag, jsDoc *ast.Node) (*ast.ParameterDeclaration, bool) {
474+
tagIndex := core.FindIndex(jsDoc.AsJSDoc().Tags.Nodes, func(n *ast.Node) bool {
475+
return n.Kind == ast.KindJSDocParameterTag && n.AsJSDocParameterOrPropertyTag() == tag
476+
})
477+
for parameterIndex, parameter := range fun.Parameters() {
478+
if parameter.Name().Kind == ast.KindIdentifier {
479+
if tag.Name().Kind == ast.KindIdentifier && parameter.Name().Text() == tag.Name().Text() {
480+
return parameter.AsParameterDeclaration(), true
481+
}
482+
} else {
483+
if parameterIndex == tagIndex {
484+
return parameter.AsParameterDeclaration(), true
485+
}
469486
}
470487
}
471488
return nil, false
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//// [tests/cases/conformance/jsdoc/jsdocDestructuringParameterDeclaration.ts] ////
2+
3+
//// [a.js]
4+
/**
5+
* @param {{ a: number; b: string }} args
6+
*/
7+
function f({ a, b }) {}
8+
9+
10+
11+
12+
//// [a.d.ts]
13+
/**
14+
* @param {{ a: number; b: string }} args
15+
*/
16+
declare function f({ a, b }: {
17+
a: number;
18+
b: string;
19+
}): void;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//// [tests/cases/conformance/jsdoc/jsdocDestructuringParameterDeclaration.ts] ////
2+
3+
=== /a.js ===
4+
/**
5+
* @param {{ a: number; b: string }} args
6+
*/
7+
function f({ a, b }) {}
8+
>f : Symbol(f, Decl(a.js, 0, 0))
9+
>a : Symbol(a, Decl(a.js, 3, 12))
10+
>b : Symbol(b, Decl(a.js, 3, 15))
11+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//// [tests/cases/conformance/jsdoc/jsdocDestructuringParameterDeclaration.ts] ////
2+
3+
=== /a.js ===
4+
/**
5+
* @param {{ a: number; b: string }} args
6+
*/
7+
function f({ a, b }) {}
8+
>f : ({ a, b }: { a: number; b: string; }) => void
9+
>a : number
10+
>b : string
11+

testdata/baselines/reference/conformance/jsdocParseAwait.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ const a = 1;
1414
/** @type {T} */
1515
const b = {
1616
>b : T
17-
>{ await: false,} : { await: boolean; }
17+
>{ await: false,} : { await: false; }
1818

1919
await: false,
20-
>await : boolean
20+
>await : false
2121
>false : false
2222

2323
};

testdata/baselines/reference/submodule/compiler/checkJsdocTypeTagOnExportAssignment8.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@
1111

1212
/** @type {Foo} */
1313
export default {
14-
>{ a: 'a', b: 'b'} : { a: string; b: string; }
14+
>{ a: 'a', b: 'b'} : { a: string; b: "b"; }
1515

1616
a: 'a',
1717
>a : string
1818
>'a' : "a"
1919

2020
b: 'b'
21-
>b : string
21+
>b : "b"
2222
>'b' : "b"
2323
}
2424

testdata/baselines/reference/submodule/compiler/declarationEmitClassSetAccessorParamNameInJs2.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,7 @@ export declare class Foo {
2020
*
2121
* @param {{ prop: string }} baz Baz.
2222
*/
23-
set bar({}: {});
23+
set bar({}: {
24+
prop: string;
25+
});
2426
}

testdata/baselines/reference/submodule/compiler/declarationEmitClassSetAccessorParamNameInJs2.js.diff

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,4 @@
88
+export declare class Foo {
99
/**
1010
* Bar.
11-
*
12-
* @param {{ prop: string }} baz Baz.
13-
*/
14-
- set bar({}: {
15-
- prop: string;
16-
- });
17-
+ set bar({}: {});
18-
}
11+
*

testdata/baselines/reference/submodule/compiler/declarationEmitClassSetAccessorParamNameInJs2.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ export class Foo {
1010
* @param {{ prop: string }} baz Baz.
1111
*/
1212
set bar({}) {}
13-
>bar : any
13+
>bar : { prop: string; }
1414
}
1515

testdata/baselines/reference/submodule/compiler/declarationEmitClassSetAccessorParamNameInJs3.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ export declare class Foo {
2121
* @param {{ prop: string | undefined }} baz Baz.
2222
*/
2323
set bar({ prop }: {
24-
prop?: string;
24+
prop: string | undefined;
2525
});
2626
}

testdata/baselines/reference/submodule/compiler/declarationEmitClassSetAccessorParamNameInJs3.js.diff

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,4 @@
88
+export declare class Foo {
99
/**
1010
* Bar.
11-
*
12-
* @param {{ prop: string | undefined }} baz Baz.
13-
*/
14-
set bar({ prop }: {
15-
- prop: string | undefined;
16-
+ prop?: string;
17-
});
18-
}
11+
*

testdata/baselines/reference/submodule/compiler/declarationEmitClassSetAccessorParamNameInJs3.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export class Foo {
1010
* @param {{ prop: string | undefined }} baz Baz.
1111
*/
1212
set bar({ prop = 'foo' }) {}
13-
>bar : any
13+
>bar : { prop: string; }
1414
>prop : string
1515
>'foo' : "foo"
1616
}

testdata/baselines/reference/submodule/compiler/jsDeclarationEmitDoesNotRenameImport.types

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,19 @@ class X extends Test {
4040
>super : typeof Test
4141

4242
if (options.test) {
43-
>options.test : any
43+
>options.test : typeof import("./Test.js").default | undefined
4444
>options : Options
45-
>test : any
45+
>test : typeof import("./Test.js").default | undefined
4646

4747
this.test = new options.test();
48-
>this.test = new options.test() : any
48+
>this.test = new options.test() : import("./Test.js").default
4949
>this.test : any
5050
>this : this
5151
>test : any
52-
>new options.test() : any
53-
>options.test : any
52+
>new options.test() : import("./Test.js").default
53+
>options.test : typeof import("./Test.js").default
5454
>options : Options
55-
>test : any
55+
>test : typeof import("./Test.js").default
5656
}
5757
}
5858
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/a.js(3,15): error TS2552: Cannot find name 'sting'. Did you mean 'string'?
2+
3+
4+
==== /a.js (1 errors) ====
5+
/**
6+
* @typedef MyType
7+
* @property {sting} [x]
8+
~~~~~
9+
!!! error TS2552: Cannot find name 'sting'. Did you mean 'string'?
10+
*/
11+
12+
/** @param {MyType} p */
13+
export function f(p) { }
14+
15+
==== /b.js (0 errors) ====
16+
import { f } from "./a.js"
17+
f({ x: 42 })
18+

testdata/baselines/reference/submodule/compiler/strictOptionalProperties3.errors.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
1+
a.js(7,7): error TS2375: Type '{ value: undefined; }' is not assignable to type 'A' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
2+
Types of property 'value' are incompatible.
3+
Type 'undefined' is not assignable to type 'number'.
14
a.js(14,7): error TS2375: Type '{ value: undefined; }' is not assignable to type 'B' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
25
Types of property 'value' are incompatible.
36
Type 'undefined' is not assignable to type 'number'.
47

58

6-
==== a.js (1 errors) ====
9+
==== a.js (2 errors) ====
710
/**
811
* @typedef {object} A
912
* @property {number} [value]
1013
*/
1114

1215
/** @type {A} */
1316
const a = { value: undefined }; // error
17+
~
18+
!!! error TS2375: Type '{ value: undefined; }' is not assignable to type 'A' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
19+
!!! error TS2375: Types of property 'value' are incompatible.
20+
!!! error TS2375: Type 'undefined' is not assignable to type 'number'.
1421

1522
/**
1623
* @typedef {{ value?: number }} B

testdata/baselines/reference/submodule/compiler/strictOptionalProperties4.types

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,17 @@ const x = /** @type {Foo} */ ({});
1212
>{} : {}
1313

1414
x.foo; // number | undefined
15-
>x.foo : any
15+
>x.foo : number | undefined
1616
>x : Foo
17-
>foo : any
17+
>foo : number | undefined
1818

1919
const y = /** @type {Required<Foo>} */ ({});
2020
>y : Required<Foo>
2121
>({}) : Required<Foo>
2222
>{} : {}
2323

2424
y.foo; // number
25-
>y.foo : any
25+
>y.foo : number
2626
>y : Required<Foo>
27-
>foo : any
27+
>foo : number
2828

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ const t3 = /** @satisfies {T1} */ ({});
4444
/** @type {T2} */
4545
const t4 = /** @satisfies {T2} */ ({ a: "a" });
4646
>t4 : T2
47-
>({ a: "a" }) : { a: string; }
48-
>{ a: "a" } : { a: string; }
49-
>a : string
47+
>({ a: "a" }) : { a: "a"; }
48+
>{ a: "a" } : { a: "a"; }
49+
>a : "a"
5050
>"a" : "a"
5151

5252
/** @type {(m: string) => string} */

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ function foo(opts) {
1616
>opts : Opts
1717

1818
opts.x;
19-
>opts.x : any
19+
>opts.x : string
2020
>opts : Opts
21-
>x : any
21+
>x : string
2222
}
2323

2424
foo({x: 'abc'});
@@ -40,9 +40,9 @@ function foo1(opts) {
4040
>opts : AnotherOpts
4141

4242
opts.anotherX;
43-
>opts.anotherX : any
43+
>opts.anotherX : string
4444
>opts : AnotherOpts
45-
>anotherX : any
45+
>anotherX : string
4646
}
4747

4848
foo1({anotherX: "world"});
@@ -66,9 +66,9 @@ function foo2(opts) {
6666
>opts : Opts1
6767

6868
opts.x;
69-
>opts.x : any
69+
>opts.x : string
7070
>opts : Opts1
71-
>x : any
71+
>x : string
7272
}
7373
foo2({x: 'abc'});
7474
>foo2({x: 'abc'}) : void

testdata/baselines/reference/submodule/conformance/destructuringParameterDeclaration9(strict=false).symbols

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export function prepareConfig({
99
>prepareConfig : Symbol(prepareConfig, Decl(index.js, 0, 0))
1010

1111
additionalFiles: {
12-
>additionalFiles : Symbol(additionalFiles)
12+
>additionalFiles : Symbol(additionalFiles, Decl(index.js, 2, 3))
1313

1414
json = []
1515
>json : Symbol(json, Decl(index.js, 5, 22))
@@ -60,7 +60,7 @@ export const prepareConfigWithContextualSignature = ({
6060
*/
6161
function f1({ a: { json = [] } = {} } = {}) { return json }
6262
>f1 : Symbol(f1, Decl(index.js, 29, 1))
63-
>a : Symbol(a)
63+
>a : Symbol(a, Decl(index.js, 34, 12))
6464
>json : Symbol(json, Decl(index.js, 36, 18))
6565
>json : Symbol(json, Decl(index.js, 36, 18))
6666

0 commit comments

Comments
 (0)