Skip to content

Commit 179ff49

Browse files
authored
Consistent rules for mixing @type, @param, @return, @template (#1979)
1 parent 24b38de commit 179ff49

File tree

10 files changed

+335
-140
lines changed

10 files changed

+335
-140
lines changed

internal/checker/checker.go

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3257,9 +3257,6 @@ func (c *Checker) checkFunctionOrMethodDeclaration(node *ast.Node) {
32573257
if c.getContextualCallSignature(c.getTypeFromTypeNode(node.FunctionLikeData().FullSignature), node) == nil {
32583258
c.error(node.FunctionLikeData().FullSignature, diagnostics.A_JSDoc_type_tag_on_a_function_must_have_a_signature_with_the_correct_number_of_arguments)
32593259
}
3260-
if node.Type() != nil || core.Some(node.Parameters(), func(p *ast.Node) bool { return p.Type() != nil }) {
3261-
c.error(node.FunctionLikeData().FullSignature, diagnostics.A_JSDoc_type_tag_may_not_occur_with_a_param_or_returns_tag)
3262-
}
32633260
}
32643261
if node.Type() == nil {
32653262
// Report an implicit any error if there is no body, no explicit return type, and node is not a private method
@@ -9830,9 +9827,6 @@ func (c *Checker) checkFunctionExpressionOrObjectLiteralMethod(node *ast.Node, c
98309827
if c.getContextualCallSignature(c.getTypeFromTypeNode(node.FunctionLikeData().FullSignature), node) == nil {
98319828
c.error(node.FunctionLikeData().FullSignature, diagnostics.A_JSDoc_type_tag_on_a_function_must_have_a_signature_with_the_correct_number_of_arguments)
98329829
}
9833-
if node.Type() != nil || core.Some(node.Parameters(), func(p *ast.Node) bool { return p.Type() != nil }) {
9834-
c.error(node.FunctionLikeData().FullSignature, diagnostics.A_JSDoc_type_tag_may_not_occur_with_a_param_or_returns_tag)
9835-
}
98369830
}
98379831
c.contextuallyCheckFunctionExpressionOrObjectLiteralMethod(node, checkMode)
98389832
return c.getTypeOfSymbol(c.getSymbolOfDeclaration(node))
@@ -11678,12 +11672,11 @@ func (c *Checker) TryGetThisTypeAtEx(node *ast.Node, includeGlobalThis bool, con
1167811672
container = c.getThisContainer(node, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/)
1167911673
}
1168011674
if ast.IsFunctionLike(container) && (!c.isInParameterInitializerBeforeContainingFunction(node) || ast.GetThisParameter(container) != nil) {
11681-
thisType := c.getThisTypeOfDeclaration(container)
11682-
if thisType == nil && ast.IsInJSFile(container) {
11683-
if sig := c.getSignatureOfFullSignatureType(container); sig != nil {
11684-
thisType = c.getThisTypeOfSignature(sig)
11685-
}
11675+
sig := c.getSignatureOfFullSignatureType(container)
11676+
if sig == nil {
11677+
sig = c.getSignatureFromDeclaration(container)
1168611678
}
11679+
thisType := c.getThisTypeOfSignature(sig)
1168711680
// Note: a parameter initializer should refer to class-this unless function-this is explicitly annotated.
1168811681
// If this is a function in a JS file, it might be a class method.
1168911682
if thisType == nil {
@@ -11782,10 +11775,6 @@ func (c *Checker) isInParameterInitializerBeforeContainingFunction(node *ast.Nod
1178211775
return false
1178311776
}
1178411777

11785-
func (c *Checker) getThisTypeOfDeclaration(declaration *ast.Node) *Type {
11786-
return c.getThisTypeOfSignature(c.getSignatureFromDeclaration(declaration))
11787-
}
11788-
1178911778
func (c *Checker) checkThisInStaticClassFieldInitializerInDecoratedClass(thisExpression *ast.Node, container *ast.Node) {
1179011779
if ast.IsPropertyDeclaration(container) && ast.HasStaticModifier(container) && c.legacyDecorators {
1179111780
initializer := container.Initializer()
@@ -19091,11 +19080,11 @@ func (c *Checker) getSignaturesOfSymbol(symbol *ast.Symbol) []*Signature {
1909119080
}
1909219081
// If this is a function or method declaration, get the signature from the @type tag for the sake of optional parameters.
1909319082
// Exclude contextually-typed kinds because we already apply the @type tag to the context, plus applying it here to the initializer would suppress checks that the two are compatible.
19094-
if sig := c.getSignatureOfFullSignatureType(decl); sig != nil {
19095-
result = append(result, sig)
19096-
continue
19083+
sig := c.getSignatureOfFullSignatureType(decl)
19084+
if sig == nil {
19085+
sig = c.getSignatureFromDeclaration(decl)
1909719086
}
19098-
result = append(result, c.getSignatureFromDeclaration(decl))
19087+
result = append(result, sig)
1909919088
}
1910019089
return result
1910119090
}
@@ -19177,15 +19166,13 @@ func (c *Checker) getSignatureFromDeclaration(declaration *ast.Node) *Signature
1917719166
}
1917819167

1917919168
func (c *Checker) getTypeParametersFromDeclaration(declaration *ast.Node) []*Type {
19169+
if sig := c.getSignatureOfFullSignatureType(declaration); sig != nil {
19170+
return sig.TypeParameters()
19171+
}
1918019172
var result []*Type
1918119173
for _, node := range declaration.TypeParameters() {
1918219174
result = core.AppendIfUnique(result, c.getDeclaredTypeOfTypeParameter(node.Symbol()))
1918319175
}
19184-
if len(result) == 0 && ast.IsFunctionDeclaration(declaration) {
19185-
if sig := c.getSignatureOfFullSignatureType(declaration); sig != nil {
19186-
return sig.TypeParameters()
19187-
}
19188-
}
1918919176
return result
1919019177
}
1919119178

internal/checker/relater.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1994,19 +1994,11 @@ func (c *Checker) getTypePredicateOfSignature(sig *Signature) *TypePredicate {
19941994
default:
19951995
if sig.declaration != nil {
19961996
typeNode := sig.declaration.Type()
1997-
var jsdocTypePredicate *TypePredicate
1998-
if typeNode == nil {
1999-
if jsdocSignature := c.getSignatureOfFullSignatureType(sig.declaration); jsdocSignature != nil {
2000-
jsdocTypePredicate = c.getTypePredicateOfSignature(jsdocSignature)
2001-
}
2002-
}
20031997
switch {
20041998
case typeNode != nil:
20051999
if ast.IsTypePredicateNode(typeNode) {
20062000
sig.resolvedTypePredicate = c.createTypePredicateFromTypePredicateNode(typeNode, sig)
20072001
}
2008-
case jsdocTypePredicate != nil:
2009-
sig.resolvedTypePredicate = jsdocTypePredicate
20102002
case ast.IsFunctionLikeDeclaration(sig.declaration) && (sig.resolvedReturnType == nil || sig.resolvedReturnType.flags&TypeFlagsBoolean != 0) && c.getParameterCount(sig) > 0:
20112003
sig.resolvedTypePredicate = c.noTypePredicate // avoid infinite loop
20122004
sig.resolvedTypePredicate = c.getTypePredicateFromBody(sig.declaration)

internal/diagnostics/diagnostics_generated.go

Lines changed: 0 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/diagnostics/extraDiagnosticMessages.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@
1919
"category": "Error",
2020
"code": 8030
2121
},
22-
"A JSDoc '@type' tag may not occur with a '@param' or '@returns' tag.": {
23-
"category": "Error",
24-
"code": 8040
25-
},
2622
"Failed to delete file '{0}'.": {
2723
"category": "Message",
2824
"code": 6353

internal/parser/reparser.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node)
336336
}
337337
if fun, ok := getFunctionLikeHost(parent); ok {
338338
noTypedParams := core.Every(fun.Parameters(), func(param *ast.Node) bool { return param.Type() == nil })
339-
if fun.Type() == nil && noTypedParams && tag.AsJSDocTypeTag().TypeExpression != nil {
339+
if fun.TypeParameterList() == nil && fun.Type() == nil && noTypedParams && tag.AsJSDocTypeTag().TypeExpression != nil {
340340
fun.FunctionLikeData().FullSignature = p.factory.DeepCloneReparse(tag.AsJSDocTypeTag().TypeExpression.Type())
341341
p.finishMutatedNode(fun)
342342
}
@@ -389,7 +389,7 @@ func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node)
389389
}
390390
case ast.KindJSDocTemplateTag:
391391
if fun, ok := getFunctionLikeHost(parent); ok {
392-
if fun.TypeParameters() == nil {
392+
if fun.TypeParameters() == nil && fun.FunctionLikeData().FullSignature == nil {
393393
fun.FunctionLikeData().TypeParameters = p.gatherTypeParameters(jsDoc, nil /*tagWithTypeParameters*/)
394394
p.finishMutatedNode(fun)
395395
}
@@ -407,7 +407,7 @@ func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node)
407407
}
408408
}
409409
case ast.KindJSDocParameterTag:
410-
if fun, ok := getFunctionLikeHost(parent); ok {
410+
if fun, ok := getFunctionLikeHost(parent); ok && fun.FunctionLikeData().FullSignature == nil {
411411
parameterTag := tag.AsJSDocParameterOrPropertyTag()
412412
if param, ok := findMatchingParameter(fun, parameterTag, jsDoc); ok {
413413
if param.Type == nil && parameterTag.TypeExpression != nil {
@@ -449,7 +449,7 @@ func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node)
449449
}
450450
}
451451
case ast.KindJSDocReturnTag:
452-
if fun, ok := getFunctionLikeHost(parent); ok {
452+
if fun, ok := getFunctionLikeHost(parent); ok && fun.FunctionLikeData().FullSignature == nil {
453453
if fun.Type() == nil && tag.AsJSDocReturnTag().TypeExpression != nil {
454454
fun.FunctionLikeData().Type = p.factory.DeepCloneReparse(tag.AsJSDocReturnTag().TypeExpression.Type())
455455
p.finishMutatedNode(fun)

testdata/baselines/reference/conformance/jsdocTypeParameterTagConflict.errors.txt

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 102 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,126 @@
11
//// [tests/cases/conformance/jsdoc/jsdocTypeParameterTagConflict.ts] ////
22

33
//// [a.js]
4+
// @type on a function is applicable when there are no preceding @param, @return, or @template annotations
5+
// and no @type parameter annotations.
6+
7+
// @param for a parameter is applicable when there is no applicable @type annotation for the function and
8+
// no @type annotation on the parameter.
9+
10+
// @return is applicable when there is no applicable @type annotation for the function.
11+
12+
// @template is applicable when there is no applicable @type annotation for the function.
13+
414
/**
5-
* @type {(a: 1) => true}
6-
* @param {2} a
15+
* @type {(a: 1, b: 2) => number}
16+
* @param {3} a
17+
* @param {4} b
18+
* @return {string}
719
*/
8-
export function conflictingParam(a) { return true }
20+
function f1(/** @type {5}*/ a, b) { return "abc" }
921

1022
/**
11-
* @type {(b: 3) => true}
12-
* @return {false}
23+
* @type {(a: 1, b: 2) => number}
24+
* @param {3} a
25+
* @param {4} b
26+
* @return {string}
1327
*/
14-
export function conflictingReturn(b) { return false }
28+
function f2(a, b) { return 42 }
1529

30+
/**
31+
* @param {3} a
32+
* @type {(a: 1, b: 2) => number}
33+
* @param {4} b
34+
* @return {string}
35+
*/
36+
function f3(a, b) { return "abc" }
37+
38+
/**
39+
* @return {string}
40+
* @type {(a: 1, b: 2) => number}
41+
* @param {3} a
42+
* @param {4} b
43+
*/
44+
function f4(a, b) { return "abc" }
45+
46+
/**
47+
* @type {(a: 1, b: 2) => number}
48+
* @template T
49+
* @template U
50+
* @param {T} a
51+
* @param {U} b
52+
* @return {string}
53+
*/
54+
function f5(a, b) { return 42 }
1655

1756
/**
18-
* @type {(c: 4) => true}
19-
* @param {5} d
20-
* @return {false}
57+
* @template T
58+
* @type {(a: 1, b: 2) => number}
59+
* @template U
60+
* @param {T} a
61+
* @param {U} b
62+
* @return {string}
2163
*/
22-
export function conflictingBoth(d) { return false }
64+
function f6(a, b) { return "abc" }
2365

66+
/**
67+
* @param {1} a
68+
* @param {2} a
69+
*/
70+
function f7(a) {}
2471

2572

2673

2774

2875
//// [a.d.ts]
2976
/**
30-
* @type {(a: 1) => true}
31-
* @param {2} a
77+
* @type {(a: 1, b: 2) => number}
78+
* @param {3} a
79+
* @param {4} b
80+
* @return {string}
3281
*/
33-
export declare function conflictingParam(a: 2): true;
82+
declare function f1(/** @type {5}*/ a: 5, b: 4): string;
3483
/**
35-
* @type {(b: 3) => true}
36-
* @return {false}
84+
* @type {(a: 1, b: 2) => number}
85+
* @param {3} a
86+
* @param {4} b
87+
* @return {string}
3788
*/
38-
export declare function conflictingReturn(b: 3): false;
89+
declare function f2(a: 1, b: 2): number;
3990
/**
40-
* @type {(c: 4) => true}
41-
* @param {5} d
42-
* @return {false}
91+
* @param {3} a
92+
* @type {(a: 1, b: 2) => number}
93+
* @param {4} b
94+
* @return {string}
95+
*/
96+
declare function f3(a: 3, b: 4): string;
97+
/**
98+
* @return {string}
99+
* @type {(a: 1, b: 2) => number}
100+
* @param {3} a
101+
* @param {4} b
102+
*/
103+
declare function f4(a: 3, b: 4): string;
104+
/**
105+
* @type {(a: 1, b: 2) => number}
106+
* @template T
107+
* @template U
108+
* @param {T} a
109+
* @param {U} b
110+
* @return {string}
111+
*/
112+
declare function f5(a: 1, b: 2): number;
113+
/**
114+
* @template T
115+
* @type {(a: 1, b: 2) => number}
116+
* @template U
117+
* @param {T} a
118+
* @param {U} b
119+
* @return {string}
120+
*/
121+
declare function f6<T, U>(a: T, b: U): string;
122+
/**
123+
* @param {1} a
124+
* @param {2} a
43125
*/
44-
export declare function conflictingBoth(d: 5): false;
126+
declare function f7(a: 1): void;

0 commit comments

Comments
 (0)