Skip to content

Commit a12ec1a

Browse files
authored
Merge pull request #15788 from Microsoft/fix15707
if a JSSpecialPropertyDeclaration has a JSDoc type, use it
2 parents 1a7c193 + 3521b9c commit a12ec1a

File tree

4 files changed

+75
-10
lines changed

4 files changed

+75
-10
lines changed

src/compiler/checker.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4158,6 +4158,7 @@ namespace ts {
41584158
const types: Type[] = [];
41594159
let definedInConstructor = false;
41604160
let definedInMethod = false;
4161+
let jsDocType: Type;
41614162
for (const declaration of symbol.declarations) {
41624163
const expression = declaration.kind === SyntaxKind.BinaryExpression ? <BinaryExpression>declaration :
41634164
declaration.kind === SyntaxKind.PropertyAccessExpression ? <BinaryExpression>getAncestor(declaration, SyntaxKind.BinaryExpression) :
@@ -4176,19 +4177,26 @@ namespace ts {
41764177
}
41774178
}
41784179

4179-
if (expression.flags & NodeFlags.JavaScriptFile) {
4180-
// If there is a JSDoc type, use it
4181-
const type = getTypeForDeclarationFromJSDocComment(expression.parent);
4182-
if (type && type !== unknownType) {
4183-
types.push(getWidenedType(type));
4184-
continue;
4180+
// If there is a JSDoc type, use it
4181+
const type = getTypeForDeclarationFromJSDocComment(expression.parent);
4182+
if (type) {
4183+
const declarationType = getWidenedType(type);
4184+
if (!jsDocType) {
4185+
jsDocType = declarationType;
4186+
}
4187+
else if (jsDocType !== unknownType && declarationType !== unknownType && !isTypeIdenticalTo(jsDocType, declarationType)) {
4188+
const name = getNameOfDeclaration(declaration);
4189+
error(name, Diagnostics.Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2, declarationNameToString(name), typeToString(jsDocType), typeToString(declarationType));
41854190
}
41864191
}
4187-
4188-
types.push(getWidenedLiteralType(checkExpressionCached(expression.right)));
4192+
else if (!jsDocType) {
4193+
// If we don't have an explicit JSDoc type, get the type from the expression.
4194+
types.push(getWidenedLiteralType(checkExpressionCached(expression.right)));
4195+
}
41894196
}
41904197

4191-
return getWidenedType(addOptionality(getUnionType(types, /*subtypeReduction*/ true), definedInMethod && !definedInConstructor));
4198+
const type = jsDocType || getUnionType(types, /*subtypeReduction*/ true);
4199+
return getWidenedType(addOptionality(type, definedInMethod && !definedInConstructor));
41924200
}
41934201

41944202
// Return the type implied by a binding pattern element. This is the type of the initializer of the element if
@@ -13229,7 +13237,7 @@ namespace ts {
1322913237
}
1323013238
return result;
1323113239
}
13232-
}
13240+
}
1323313241

1323413242
function isValidSpreadType(type: Type): boolean {
1323513243
return !!(type.flags & (TypeFlags.Any | TypeFlags.Null | TypeFlags.Undefined | TypeFlags.NonPrimitive) ||
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
=== tests/cases/compiler/a.js ===
2+
class C {
3+
>C : Symbol(C, Decl(a.js, 0, 0))
4+
5+
constructor() {
6+
/** @type {boolean} */
7+
this.a = true;
8+
>this.a : Symbol(C.a, Decl(a.js, 1, 16), Decl(a.js, 3, 16))
9+
>this : Symbol(C, Decl(a.js, 0, 0))
10+
>a : Symbol(C.a, Decl(a.js, 1, 16), Decl(a.js, 3, 16))
11+
12+
this.a = !!this.a;
13+
>this.a : Symbol(C.a, Decl(a.js, 1, 16), Decl(a.js, 3, 16))
14+
>this : Symbol(C, Decl(a.js, 0, 0))
15+
>a : Symbol(C.a, Decl(a.js, 1, 16), Decl(a.js, 3, 16))
16+
>this.a : Symbol(C.a, Decl(a.js, 1, 16), Decl(a.js, 3, 16))
17+
>this : Symbol(C, Decl(a.js, 0, 0))
18+
>a : Symbol(C.a, Decl(a.js, 1, 16), Decl(a.js, 3, 16))
19+
}
20+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
=== tests/cases/compiler/a.js ===
2+
class C {
3+
>C : C
4+
5+
constructor() {
6+
/** @type {boolean} */
7+
this.a = true;
8+
>this.a = true : true
9+
>this.a : boolean
10+
>this : this
11+
>a : boolean
12+
>true : true
13+
14+
this.a = !!this.a;
15+
>this.a = !!this.a : boolean
16+
>this.a : boolean
17+
>this : this
18+
>a : boolean
19+
>!!this.a : boolean
20+
>!this.a : boolean
21+
>this.a : true
22+
>this : this
23+
>a : true
24+
}
25+
}

tests/cases/compiler/checkJsFiles7.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// @allowJs: true
2+
// @checkJs: true
3+
// @noEmit: true
4+
// @noImplicitAny: true
5+
// @fileName: a.js
6+
class C {
7+
constructor() {
8+
/** @type {boolean} */
9+
this.a = true;
10+
this.a = !!this.a;
11+
}
12+
}

0 commit comments

Comments
 (0)