Skip to content

Commit 007b82f

Browse files
authored
Better checking of @param/@Property tags (#39487)
* More consistent checking of @property/@param 1. Use getWidenedTypeForVariableLikeDeclaration, instead of directly calling tryGetTypeFromEffectiveTypeNode. This requires some changes in the former function since it can't assume that the declaration has an initializer. 2. isOptional now calls isOptionalJSDocPropertyLikeTag. 3. isOptionalJSDocPropertyLikeTag now handles JSDocPropertyTag (previously it was named isOptionalJSDocParameterTag). * rename to isOptionalJSDocPropertyLikeTag
1 parent 48c19ee commit 007b82f

File tree

2 files changed

+28
-27
lines changed

2 files changed

+28
-27
lines changed

src/compiler/checker.ts

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7737,7 +7737,7 @@ namespace ts {
77377737
}
77387738

77397739
// Return the inferred type for a variable, parameter, or property declaration
7740-
function getTypeForVariableLikeDeclaration(declaration: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement, includeOptionality: boolean): Type | undefined {
7740+
function getTypeForVariableLikeDeclaration(declaration: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement | JSDocPropertyLikeTag, includeOptionality: boolean): Type | undefined {
77417741
// A variable declared in a for..in statement is of type string, or of type keyof T when the
77427742
// right hand expression is of a type parameter type.
77437743
if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === SyntaxKind.ForInStatement) {
@@ -7760,6 +7760,7 @@ namespace ts {
77607760

77617761
const isOptional = includeOptionality && (
77627762
isParameter(declaration) && isJSDocOptionalParameter(declaration)
7763+
|| isOptionalJSDocPropertyLikeTag(declaration)
77637764
|| !isBindingElement(declaration) && !isVariableDeclaration(declaration) && !!declaration.questionToken);
77647765

77657766
// Use type from type annotation if one is present
@@ -7769,7 +7770,7 @@ namespace ts {
77697770
}
77707771

77717772
if ((noImplicitAny || isInJSFile(declaration)) &&
7772-
declaration.kind === SyntaxKind.VariableDeclaration && !isBindingPattern(declaration.name) &&
7773+
isVariableDeclaration(declaration) && !isBindingPattern(declaration.name) &&
77737774
!(getCombinedModifierFlags(declaration) & ModifierFlags.Export) && !(declaration.flags & NodeFlags.Ambient)) {
77747775
// If --noImplicitAny is on or the declaration is in a Javascript file,
77757776
// use control flow tracked 'any' type for non-ambient, non-exported var or let variables with no
@@ -7784,7 +7785,7 @@ namespace ts {
77847785
}
77857786
}
77867787

7787-
if (declaration.kind === SyntaxKind.Parameter) {
7788+
if (isParameter(declaration)) {
77887789
const func = <FunctionLikeDeclaration>declaration.parent;
77897790
// For a parameter of a set accessor, use the type of the get accessor if one is present
77907791
if (func.kind === SyntaxKind.SetAccessor && !hasNonBindableDynamicName(func)) {
@@ -7814,16 +7815,16 @@ namespace ts {
78147815
return addOptionality(type, isOptional);
78157816
}
78167817
}
7817-
else if (isInJSFile(declaration)) {
7818-
const containerObjectType = getJSContainerObjectType(declaration, getSymbolOfNode(declaration), getDeclaredExpandoInitializer(declaration));
7819-
if (containerObjectType) {
7820-
return containerObjectType;
7821-
}
7822-
}
78237818

78247819
// Use the type of the initializer expression if one is present and the declaration is
78257820
// not a parameter of a contextually typed function
7826-
if (declaration.initializer) {
7821+
if (hasOnlyExpressionInitializer(declaration) && !!declaration.initializer) {
7822+
if (isInJSFile(declaration) && !isParameter(declaration)) {
7823+
const containerObjectType = getJSContainerObjectType(declaration, getSymbolOfNode(declaration), getDeclaredExpandoInitializer(declaration));
7824+
if (containerObjectType) {
7825+
return containerObjectType;
7826+
}
7827+
}
78277828
const type = widenTypeInferredFromInitializer(declaration, checkDeclarationInitializer(declaration));
78287829
return addOptionality(type, isOptional);
78297830
}
@@ -8243,7 +8244,7 @@ namespace ts {
82438244
// Here, the array literal [1, "one"] is contextually typed by the type [any, string], which is the implied type of the
82448245
// binding pattern [x, s = ""]. Because the contextual type is a tuple type, the resulting type of [1, "one"] is the
82458246
// tuple type [number, string]. Thus, the type inferred for 'x' is number and the type inferred for 's' is string.
8246-
function getWidenedTypeForVariableLikeDeclaration(declaration: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement, reportErrors?: boolean): Type {
8247+
function getWidenedTypeForVariableLikeDeclaration(declaration: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement | JSDocPropertyLikeTag, reportErrors?: boolean): Type {
82478248
return widenTypeForVariableLikeDeclaration(getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true), declaration, reportErrors);
82488249
}
82498250

@@ -8350,8 +8351,7 @@ namespace ts {
83508351
(isCallExpression(declaration) || (isPropertyAccessExpression(declaration) || isBindableStaticElementAccessExpression(declaration)) && isBinaryExpression(declaration.parent)))) {
83518352
type = getWidenedTypeForAssignmentDeclaration(symbol);
83528353
}
8353-
else if (isJSDocPropertyLikeTag(declaration)
8354-
|| isPropertyAccessExpression(declaration)
8354+
else if (isPropertyAccessExpression(declaration)
83558355
|| isElementAccessExpression(declaration)
83568356
|| isIdentifier(declaration)
83578357
|| isStringLiteralLike(declaration)
@@ -8385,7 +8385,8 @@ namespace ts {
83858385
|| isPropertyDeclaration(declaration)
83868386
|| isPropertySignature(declaration)
83878387
|| isVariableDeclaration(declaration)
8388-
|| isBindingElement(declaration)) {
8388+
|| isBindingElement(declaration)
8389+
|| isJSDocPropertyLikeTag(declaration)) {
83898390
type = getWidenedTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true);
83908391
}
83918392
// getTypeOfSymbol dispatches some JS merges incorrectly because their symbol flags are not mutually exclusive.
@@ -11171,8 +11172,8 @@ namespace ts {
1117111172
return symbol && withAugmentations ? getMergedSymbol(symbol) : symbol;
1117211173
}
1117311174

11174-
function isOptionalParameter(node: ParameterDeclaration | JSDocParameterTag) {
11175-
if (hasQuestionToken(node) || isOptionalJSDocParameterTag(node) || isJSDocOptionalParameter(node)) {
11175+
function isOptionalParameter(node: ParameterDeclaration | JSDocParameterTag | JSDocPropertyTag) {
11176+
if (hasQuestionToken(node) || isOptionalJSDocPropertyLikeTag(node) || isJSDocOptionalParameter(node)) {
1117611177
return true;
1117711178
}
1117811179

@@ -11192,8 +11193,8 @@ namespace ts {
1119211193
return false;
1119311194
}
1119411195

11195-
function isOptionalJSDocParameterTag(node: Node): node is JSDocParameterTag {
11196-
if (!isJSDocParameterTag(node)) {
11196+
function isOptionalJSDocPropertyLikeTag(node: Node): node is JSDocPropertyLikeTag {
11197+
if (!isJSDocPropertyLikeTag(node)) {
1119711198
return false;
1119811199
}
1119911200
const { isBracketed, typeExpression } = node;
@@ -11301,7 +11302,7 @@ namespace ts {
1130111302
}
1130211303

1130311304
// Record a new minimum argument count if this is not an optional parameter
11304-
const isOptionalParameter = isOptionalJSDocParameterTag(param) ||
11305+
const isOptionalParameter = isOptionalJSDocPropertyLikeTag(param) ||
1130511306
param.initializer || param.questionToken || param.dotDotDotToken ||
1130611307
iife && parameters.length > iife.arguments.length && !type ||
1130711308
isJSDocOptionalParameter(param);

tests/baselines/reference/jsdocParamTagTypeLiteral.types

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,18 @@ normal(12);
2323
* @param {string} [opts1.w="hi"] doc5
2424
*/
2525
function foo1(opts1) {
26-
>foo1 : (opts1: { x: string; y?: string | undefined; z: string; w: string;}) => void
27-
>opts1 : { x: string; y?: string | undefined; z?: string; w?: string; }
26+
>foo1 : (opts1: { x: string; y?: string | undefined; z: string | undefined; w: string | undefined;}) => void
27+
>opts1 : { x: string; y?: string | undefined; z?: string | undefined; w?: string | undefined; }
2828

2929
opts1.x;
3030
>opts1.x : string
31-
>opts1 : { x: string; y?: string | undefined; z?: string; w?: string; }
31+
>opts1 : { x: string; y?: string | undefined; z?: string | undefined; w?: string | undefined; }
3232
>x : string
3333
}
3434

3535
foo1({x: 'abc'});
3636
>foo1({x: 'abc'}) : void
37-
>foo1 : (opts1: { x: string; y?: string | undefined; z?: string; w?: string; }) => void
37+
>foo1 : (opts1: { x: string; y?: string | undefined; z?: string | undefined; w?: string | undefined; }) => void
3838
>{x: 'abc'} : { x: string; }
3939
>x : string
4040
>'abc' : "abc"
@@ -93,19 +93,19 @@ foo3({x: 'abc'});
9393
*/
9494
function foo4(opts4) {
9595
>foo4 : (opts4: { x: string; y?: string | undefined; z: string; w: string;}) => void
96-
>opts4 : { x: string; y?: string | undefined; z?: string; w?: string; }[]
96+
>opts4 : { x: string; y?: string | undefined; z?: string | undefined; w?: string | undefined; }[]
9797

9898
opts4[0].x;
9999
>opts4[0].x : string
100-
>opts4[0] : { x: string; y?: string | undefined; z?: string; w?: string; }
101-
>opts4 : { x: string; y?: string | undefined; z?: string; w?: string; }[]
100+
>opts4[0] : { x: string; y?: string | undefined; z?: string | undefined; w?: string | undefined; }
101+
>opts4 : { x: string; y?: string | undefined; z?: string | undefined; w?: string | undefined; }[]
102102
>0 : 0
103103
>x : string
104104
}
105105

106106
foo4([{ x: 'hi' }]);
107107
>foo4([{ x: 'hi' }]) : void
108-
>foo4 : (opts4: { x: string; y?: string | undefined; z?: string; w?: string; }[]) => void
108+
>foo4 : (opts4: { x: string; y?: string | undefined; z?: string | undefined; w?: string | undefined; }[]) => void
109109
>[{ x: 'hi' }] : { x: string; }[]
110110
>{ x: 'hi' } : { x: string; }
111111
>x : string

0 commit comments

Comments
 (0)