Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8a1cd33

Browse filesBrowse files
authored
Use jsdoc casts (microsoft#17251)
* Allow jsdoc casts of parenthesized expressions * Feedback from microsoft#17211
1 parent de9a67f commit 8a1cd33
Copy full SHA for 8a1cd33

6 files changed

+382-7Lines changed: 382 additions & 7 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/compiler/checker.ts‎

Copy file name to clipboardExpand all lines: src/compiler/checker.ts
+21-5Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16256,15 +16256,19 @@ namespace ts {
1625616256
}
1625716257

1625816258
function checkAssertion(node: AssertionExpression) {
16259-
const exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(checkExpression(node.expression)));
16259+
return checkAssertionWorker(node, node.type, node.expression);
16260+
}
1626016261

16261-
checkSourceElement(node.type);
16262-
const targetType = getTypeFromTypeNode(node.type);
16262+
function checkAssertionWorker(errNode: Node, type: TypeNode, expression: UnaryExpression | Expression, checkMode?: CheckMode) {
16263+
const exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(checkExpression(expression, checkMode)));
16264+
16265+
checkSourceElement(type);
16266+
const targetType = getTypeFromTypeNode(type);
1626316267

1626416268
if (produceDiagnostics && targetType !== unknownType) {
1626516269
const widenedType = getWidenedType(exprType);
1626616270
if (!isTypeComparableTo(targetType, widenedType)) {
16267-
checkTypeComparableTo(exprType, targetType, node, Diagnostics.Type_0_cannot_be_converted_to_type_1);
16271+
checkTypeComparableTo(exprType, targetType, errNode, Diagnostics.Type_0_cannot_be_converted_to_type_1);
1626816272
}
1626916273
}
1627016274
return targetType;
@@ -17735,6 +17739,18 @@ namespace ts {
1773517739
return type;
1773617740
}
1773717741

17742+
function checkParenthesizedExpression(node: ParenthesizedExpression, checkMode?: CheckMode): Type {
17743+
if (isInJavaScriptFile(node) && node.jsDoc) {
17744+
const typecasts = flatMap(node.jsDoc, doc => filter(doc.tags, tag => tag.kind === SyntaxKind.JSDocTypeTag));
17745+
if (typecasts && typecasts.length) {
17746+
// We should have already issued an error if there were multiple type jsdocs
17747+
const cast = typecasts[0] as JSDocTypeTag;
17748+
return checkAssertionWorker(cast, cast.typeExpression.type, node.expression, checkMode);
17749+
}
17750+
}
17751+
return checkExpression(node.expression, checkMode);
17752+
}
17753+
1773817754
function checkExpressionWorker(node: Expression, checkMode: CheckMode): Type {
1773917755
switch (node.kind) {
1774017756
case SyntaxKind.Identifier:
@@ -17774,7 +17790,7 @@ namespace ts {
1777417790
case SyntaxKind.TaggedTemplateExpression:
1777517791
return checkTaggedTemplateExpression(<TaggedTemplateExpression>node);
1777617792
case SyntaxKind.ParenthesizedExpression:
17777-
return checkExpression((<ParenthesizedExpression>node).expression, checkMode);
17793+
return checkParenthesizedExpression(<ParenthesizedExpression>node, checkMode);
1777817794
case SyntaxKind.ClassExpression:
1777917795
return checkClassExpression(<ClassExpression>node);
1778017796
case SyntaxKind.FunctionExpression:
Collapse file

‎src/compiler/parser.ts‎

Copy file name to clipboardExpand all lines: src/compiler/parser.ts
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4342,7 +4342,7 @@ namespace ts {
43424342
parseExpected(SyntaxKind.OpenParenToken);
43434343
node.expression = allowInAnd(parseExpression);
43444344
parseExpected(SyntaxKind.CloseParenToken);
4345-
return finishNode(node);
4345+
return addJSDocComment(finishNode(node));
43464346
}
43474347

43484348
function parseSpreadElement(): Expression {
Collapse file

‎src/compiler/utilities.ts‎

Copy file name to clipboardExpand all lines: src/compiler/utilities.ts
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,8 @@ namespace ts {
640640
const commentRanges = (node.kind === SyntaxKind.Parameter ||
641641
node.kind === SyntaxKind.TypeParameter ||
642642
node.kind === SyntaxKind.FunctionExpression ||
643-
node.kind === SyntaxKind.ArrowFunction) ?
643+
node.kind === SyntaxKind.ArrowFunction ||
644+
node.kind === SyntaxKind.ParenthesizedExpression) ?
644645
concatenate(getTrailingCommentRanges(text, node.pos), getLeadingCommentRanges(text, node.pos)) :
645646
getLeadingCommentRangesOfNodeFromText(node, text);
646647
// True if the comment starts with '/**' but not if it is '/**/'
Collapse file
+129Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
tests/cases/conformance/jsdoc/b.js(4,13): error TS2352: Type 'number' cannot be converted to type 'string'.
2+
tests/cases/conformance/jsdoc/b.js(45,16): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeBase'.
3+
Property 'p' is missing in type 'SomeOther'.
4+
tests/cases/conformance/jsdoc/b.js(49,19): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeDerived'.
5+
Property 'x' is missing in type 'SomeOther'.
6+
tests/cases/conformance/jsdoc/b.js(51,17): error TS2352: Type 'SomeDerived' cannot be converted to type 'SomeOther'.
7+
Property 'q' is missing in type 'SomeDerived'.
8+
tests/cases/conformance/jsdoc/b.js(52,17): error TS2352: Type 'SomeBase' cannot be converted to type 'SomeOther'.
9+
Property 'q' is missing in type 'SomeBase'.
10+
tests/cases/conformance/jsdoc/b.js(58,1): error TS2322: Type '{ p: string | number | undefined; }' is not assignable to type 'SomeBase'.
11+
Types of property 'p' are incompatible.
12+
Type 'string | number | undefined' is not assignable to type 'number'.
13+
Type 'undefined' is not assignable to type 'number'.
14+
tests/cases/conformance/jsdoc/b.js(66,8): error TS2352: Type 'boolean' cannot be converted to type 'string | number'.
15+
tests/cases/conformance/jsdoc/b.js(66,15): error TS2304: Cannot find name 'numOrStr'.
16+
tests/cases/conformance/jsdoc/b.js(66,24): error TS1005: '}' expected.
17+
tests/cases/conformance/jsdoc/b.js(66,38): error TS2454: Variable 'numOrStr' is used before being assigned.
18+
tests/cases/conformance/jsdoc/b.js(67,2): error TS2322: Type 'string | number' is not assignable to type 'string'.
19+
Type 'number' is not assignable to type 'string'.
20+
tests/cases/conformance/jsdoc/b.js(67,8): error TS2454: Variable 'numOrStr' is used before being assigned.
21+
22+
23+
==== tests/cases/conformance/jsdoc/a.ts (0 errors) ====
24+
var W: string;
25+
26+
==== tests/cases/conformance/jsdoc/b.js (12 errors) ====
27+
// @ts-check
28+
var W = /** @type {string} */(/** @type {*} */ (4));
29+
30+
var W = /** @type {string} */(4); // Error
31+
~~~~~~~~~~~~~~
32+
!!! error TS2352: Type 'number' cannot be converted to type 'string'.
33+
34+
/** @type {*} */
35+
var a;
36+
37+
/** @type {string} */
38+
var s;
39+
40+
var a = /** @type {*} */("" + 4);
41+
var s = "" + /** @type {*} */(4);
42+
43+
class SomeBase {
44+
constructor() {
45+
this.p = 42;
46+
}
47+
}
48+
class SomeDerived extends SomeBase {
49+
constructor() {
50+
super();
51+
this.x = 42;
52+
}
53+
}
54+
class SomeOther {
55+
constructor() {
56+
this.q = 42;
57+
}
58+
}
59+
60+
function SomeFakeClass() {
61+
/** @type {string|number} */
62+
this.p = "bar";
63+
}
64+
65+
// Type assertion should check for assignability in either direction
66+
var someBase = new SomeBase();
67+
var someDerived = new SomeDerived();
68+
var someOther = new SomeOther();
69+
var someFakeClass = new SomeFakeClass();
70+
71+
someBase = /** @type {SomeBase} */(someDerived);
72+
someBase = /** @type {SomeBase} */(someBase);
73+
someBase = /** @type {SomeBase} */(someOther); // Error
74+
~~~~~~~~~~~~~~~~
75+
!!! error TS2352: Type 'SomeOther' cannot be converted to type 'SomeBase'.
76+
!!! error TS2352: Property 'p' is missing in type 'SomeOther'.
77+
78+
someDerived = /** @type {SomeDerived} */(someDerived);
79+
someDerived = /** @type {SomeDerived} */(someBase);
80+
someDerived = /** @type {SomeDerived} */(someOther); // Error
81+
~~~~~~~~~~~~~~~~~~~
82+
!!! error TS2352: Type 'SomeOther' cannot be converted to type 'SomeDerived'.
83+
!!! error TS2352: Property 'x' is missing in type 'SomeOther'.
84+
85+
someOther = /** @type {SomeOther} */(someDerived); // Error
86+
~~~~~~~~~~~~~~~~~
87+
!!! error TS2352: Type 'SomeDerived' cannot be converted to type 'SomeOther'.
88+
!!! error TS2352: Property 'q' is missing in type 'SomeDerived'.
89+
someOther = /** @type {SomeOther} */(someBase); // Error
90+
~~~~~~~~~~~~~~~~~
91+
!!! error TS2352: Type 'SomeBase' cannot be converted to type 'SomeOther'.
92+
!!! error TS2352: Property 'q' is missing in type 'SomeBase'.
93+
someOther = /** @type {SomeOther} */(someOther);
94+
95+
someFakeClass = someBase;
96+
someFakeClass = someDerived;
97+
98+
someBase = someFakeClass; // Error
99+
~~~~~~~~
100+
!!! error TS2322: Type '{ p: string | number | undefined; }' is not assignable to type 'SomeBase'.
101+
!!! error TS2322: Types of property 'p' are incompatible.
102+
!!! error TS2322: Type 'string | number | undefined' is not assignable to type 'number'.
103+
!!! error TS2322: Type 'undefined' is not assignable to type 'number'.
104+
someBase = /** @type {SomeBase} */(someFakeClass);
105+
106+
// Type assertion cannot be a type-predicate type
107+
/** @type {number | string} */
108+
var numOrStr;
109+
/** @type {string} */
110+
var str;
111+
if(/** @type {numOrStr is string} */(numOrStr === undefined)) { // Error
112+
~~~~~~~~~~~~~~~
113+
!!! error TS2352: Type 'boolean' cannot be converted to type 'string | number'.
114+
~~~~~~~~
115+
!!! error TS2304: Cannot find name 'numOrStr'.
116+
~~
117+
!!! error TS1005: '}' expected.
118+
~~~~~~~~
119+
!!! error TS2454: Variable 'numOrStr' is used before being assigned.
120+
str = numOrStr; // Error, no narrowing occurred
121+
~~~
122+
!!! error TS2322: Type 'string | number' is not assignable to type 'string'.
123+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
124+
~~~~~~~~
125+
!!! error TS2454: Variable 'numOrStr' is used before being assigned.
126+
}
127+
128+
129+
Collapse file
+151Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
//// [tests/cases/conformance/jsdoc/jsdocTypeTagCast.ts] ////
2+
3+
//// [a.ts]
4+
var W: string;
5+
6+
//// [b.js]
7+
// @ts-check
8+
var W = /** @type {string} */(/** @type {*} */ (4));
9+
10+
var W = /** @type {string} */(4); // Error
11+
12+
/** @type {*} */
13+
var a;
14+
15+
/** @type {string} */
16+
var s;
17+
18+
var a = /** @type {*} */("" + 4);
19+
var s = "" + /** @type {*} */(4);
20+
21+
class SomeBase {
22+
constructor() {
23+
this.p = 42;
24+
}
25+
}
26+
class SomeDerived extends SomeBase {
27+
constructor() {
28+
super();
29+
this.x = 42;
30+
}
31+
}
32+
class SomeOther {
33+
constructor() {
34+
this.q = 42;
35+
}
36+
}
37+
38+
function SomeFakeClass() {
39+
/** @type {string|number} */
40+
this.p = "bar";
41+
}
42+
43+
// Type assertion should check for assignability in either direction
44+
var someBase = new SomeBase();
45+
var someDerived = new SomeDerived();
46+
var someOther = new SomeOther();
47+
var someFakeClass = new SomeFakeClass();
48+
49+
someBase = /** @type {SomeBase} */(someDerived);
50+
someBase = /** @type {SomeBase} */(someBase);
51+
someBase = /** @type {SomeBase} */(someOther); // Error
52+
53+
someDerived = /** @type {SomeDerived} */(someDerived);
54+
someDerived = /** @type {SomeDerived} */(someBase);
55+
someDerived = /** @type {SomeDerived} */(someOther); // Error
56+
57+
someOther = /** @type {SomeOther} */(someDerived); // Error
58+
someOther = /** @type {SomeOther} */(someBase); // Error
59+
someOther = /** @type {SomeOther} */(someOther);
60+
61+
someFakeClass = someBase;
62+
someFakeClass = someDerived;
63+
64+
someBase = someFakeClass; // Error
65+
someBase = /** @type {SomeBase} */(someFakeClass);
66+
67+
// Type assertion cannot be a type-predicate type
68+
/** @type {number | string} */
69+
var numOrStr;
70+
/** @type {string} */
71+
var str;
72+
if(/** @type {numOrStr is string} */(numOrStr === undefined)) { // Error
73+
str = numOrStr; // Error, no narrowing occurred
74+
}
75+
76+
77+
78+
79+
//// [a.js]
80+
var W;
81+
//// [b.js]
82+
var __extends = (this && this.__extends) || (function () {
83+
var extendStatics = Object.setPrototypeOf ||
84+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
85+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
86+
return function (d, b) {
87+
extendStatics(d, b);
88+
function __() { this.constructor = d; }
89+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
90+
};
91+
})();
92+
// @ts-check
93+
var W = ((4));
94+
var W = (4); // Error
95+
/** @type {*} */
96+
var a;
97+
/** @type {string} */
98+
var s;
99+
var a = ("" + 4);
100+
var s = "" + (4);
101+
var SomeBase = (function () {
102+
function SomeBase() {
103+
this.p = 42;
104+
}
105+
return SomeBase;
106+
}());
107+
var SomeDerived = (function (_super) {
108+
__extends(SomeDerived, _super);
109+
function SomeDerived() {
110+
var _this = _super.call(this) || this;
111+
_this.x = 42;
112+
return _this;
113+
}
114+
return SomeDerived;
115+
}(SomeBase));
116+
var SomeOther = (function () {
117+
function SomeOther() {
118+
this.q = 42;
119+
}
120+
return SomeOther;
121+
}());
122+
function SomeFakeClass() {
123+
/** @type {string|number} */
124+
this.p = "bar";
125+
}
126+
// Type assertion should check for assignability in either direction
127+
var someBase = new SomeBase();
128+
var someDerived = new SomeDerived();
129+
var someOther = new SomeOther();
130+
var someFakeClass = new SomeFakeClass();
131+
someBase = (someDerived);
132+
someBase = (someBase);
133+
someBase = (someOther); // Error
134+
someDerived = (someDerived);
135+
someDerived = (someBase);
136+
someDerived = (someOther); // Error
137+
someOther = (someDerived); // Error
138+
someOther = (someBase); // Error
139+
someOther = (someOther);
140+
someFakeClass = someBase;
141+
someFakeClass = someDerived;
142+
someBase = someFakeClass; // Error
143+
someBase = (someFakeClass);
144+
// Type assertion cannot be a type-predicate type
145+
/** @type {number | string} */
146+
var numOrStr;
147+
/** @type {string} */
148+
var str;
149+
if ((numOrStr === undefined)) {
150+
str = numOrStr; // Error, no narrowing occurred
151+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.