mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-09 02:30:15 -06:00
Use jsdoc casts (#17251)
* Allow jsdoc casts of parenthesized expressions * Feedback from #17211
This commit is contained in:
parent
de9a67f2f3
commit
8a1cd33451
@ -16256,15 +16256,19 @@ namespace ts {
|
||||
}
|
||||
|
||||
function checkAssertion(node: AssertionExpression) {
|
||||
const exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(checkExpression(node.expression)));
|
||||
return checkAssertionWorker(node, node.type, node.expression);
|
||||
}
|
||||
|
||||
checkSourceElement(node.type);
|
||||
const targetType = getTypeFromTypeNode(node.type);
|
||||
function checkAssertionWorker(errNode: Node, type: TypeNode, expression: UnaryExpression | Expression, checkMode?: CheckMode) {
|
||||
const exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(checkExpression(expression, checkMode)));
|
||||
|
||||
checkSourceElement(type);
|
||||
const targetType = getTypeFromTypeNode(type);
|
||||
|
||||
if (produceDiagnostics && targetType !== unknownType) {
|
||||
const widenedType = getWidenedType(exprType);
|
||||
if (!isTypeComparableTo(targetType, widenedType)) {
|
||||
checkTypeComparableTo(exprType, targetType, node, Diagnostics.Type_0_cannot_be_converted_to_type_1);
|
||||
checkTypeComparableTo(exprType, targetType, errNode, Diagnostics.Type_0_cannot_be_converted_to_type_1);
|
||||
}
|
||||
}
|
||||
return targetType;
|
||||
@ -17735,6 +17739,18 @@ namespace ts {
|
||||
return type;
|
||||
}
|
||||
|
||||
function checkParenthesizedExpression(node: ParenthesizedExpression, checkMode?: CheckMode): Type {
|
||||
if (isInJavaScriptFile(node) && node.jsDoc) {
|
||||
const typecasts = flatMap(node.jsDoc, doc => filter(doc.tags, tag => tag.kind === SyntaxKind.JSDocTypeTag));
|
||||
if (typecasts && typecasts.length) {
|
||||
// We should have already issued an error if there were multiple type jsdocs
|
||||
const cast = typecasts[0] as JSDocTypeTag;
|
||||
return checkAssertionWorker(cast, cast.typeExpression.type, node.expression, checkMode);
|
||||
}
|
||||
}
|
||||
return checkExpression(node.expression, checkMode);
|
||||
}
|
||||
|
||||
function checkExpressionWorker(node: Expression, checkMode: CheckMode): Type {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
@ -17774,7 +17790,7 @@ namespace ts {
|
||||
case SyntaxKind.TaggedTemplateExpression:
|
||||
return checkTaggedTemplateExpression(<TaggedTemplateExpression>node);
|
||||
case SyntaxKind.ParenthesizedExpression:
|
||||
return checkExpression((<ParenthesizedExpression>node).expression, checkMode);
|
||||
return checkParenthesizedExpression(<ParenthesizedExpression>node, checkMode);
|
||||
case SyntaxKind.ClassExpression:
|
||||
return checkClassExpression(<ClassExpression>node);
|
||||
case SyntaxKind.FunctionExpression:
|
||||
|
||||
@ -4342,7 +4342,7 @@ namespace ts {
|
||||
parseExpected(SyntaxKind.OpenParenToken);
|
||||
node.expression = allowInAnd(parseExpression);
|
||||
parseExpected(SyntaxKind.CloseParenToken);
|
||||
return finishNode(node);
|
||||
return addJSDocComment(finishNode(node));
|
||||
}
|
||||
|
||||
function parseSpreadElement(): Expression {
|
||||
|
||||
@ -640,7 +640,8 @@ namespace ts {
|
||||
const commentRanges = (node.kind === SyntaxKind.Parameter ||
|
||||
node.kind === SyntaxKind.TypeParameter ||
|
||||
node.kind === SyntaxKind.FunctionExpression ||
|
||||
node.kind === SyntaxKind.ArrowFunction) ?
|
||||
node.kind === SyntaxKind.ArrowFunction ||
|
||||
node.kind === SyntaxKind.ParenthesizedExpression) ?
|
||||
concatenate(getTrailingCommentRanges(text, node.pos), getLeadingCommentRanges(text, node.pos)) :
|
||||
getLeadingCommentRangesOfNodeFromText(node, text);
|
||||
// True if the comment starts with '/**' but not if it is '/**/'
|
||||
|
||||
129
tests/baselines/reference/jsdocTypeTagCast.errors.txt
Normal file
129
tests/baselines/reference/jsdocTypeTagCast.errors.txt
Normal file
@ -0,0 +1,129 @@
|
||||
tests/cases/conformance/jsdoc/b.js(4,13): error TS2352: Type 'number' cannot be converted to type 'string'.
|
||||
tests/cases/conformance/jsdoc/b.js(45,16): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeBase'.
|
||||
Property 'p' is missing in type 'SomeOther'.
|
||||
tests/cases/conformance/jsdoc/b.js(49,19): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeDerived'.
|
||||
Property 'x' is missing in type 'SomeOther'.
|
||||
tests/cases/conformance/jsdoc/b.js(51,17): error TS2352: Type 'SomeDerived' cannot be converted to type 'SomeOther'.
|
||||
Property 'q' is missing in type 'SomeDerived'.
|
||||
tests/cases/conformance/jsdoc/b.js(52,17): error TS2352: Type 'SomeBase' cannot be converted to type 'SomeOther'.
|
||||
Property 'q' is missing in type 'SomeBase'.
|
||||
tests/cases/conformance/jsdoc/b.js(58,1): error TS2322: Type '{ p: string | number | undefined; }' is not assignable to type 'SomeBase'.
|
||||
Types of property 'p' are incompatible.
|
||||
Type 'string | number | undefined' is not assignable to type 'number'.
|
||||
Type 'undefined' is not assignable to type 'number'.
|
||||
tests/cases/conformance/jsdoc/b.js(66,8): error TS2352: Type 'boolean' cannot be converted to type 'string | number'.
|
||||
tests/cases/conformance/jsdoc/b.js(66,15): error TS2304: Cannot find name 'numOrStr'.
|
||||
tests/cases/conformance/jsdoc/b.js(66,24): error TS1005: '}' expected.
|
||||
tests/cases/conformance/jsdoc/b.js(66,38): error TS2454: Variable 'numOrStr' is used before being assigned.
|
||||
tests/cases/conformance/jsdoc/b.js(67,2): error TS2322: Type 'string | number' is not assignable to type 'string'.
|
||||
Type 'number' is not assignable to type 'string'.
|
||||
tests/cases/conformance/jsdoc/b.js(67,8): error TS2454: Variable 'numOrStr' is used before being assigned.
|
||||
|
||||
|
||||
==== tests/cases/conformance/jsdoc/a.ts (0 errors) ====
|
||||
var W: string;
|
||||
|
||||
==== tests/cases/conformance/jsdoc/b.js (12 errors) ====
|
||||
// @ts-check
|
||||
var W = /** @type {string} */(/** @type {*} */ (4));
|
||||
|
||||
var W = /** @type {string} */(4); // Error
|
||||
~~~~~~~~~~~~~~
|
||||
!!! error TS2352: Type 'number' cannot be converted to type 'string'.
|
||||
|
||||
/** @type {*} */
|
||||
var a;
|
||||
|
||||
/** @type {string} */
|
||||
var s;
|
||||
|
||||
var a = /** @type {*} */("" + 4);
|
||||
var s = "" + /** @type {*} */(4);
|
||||
|
||||
class SomeBase {
|
||||
constructor() {
|
||||
this.p = 42;
|
||||
}
|
||||
}
|
||||
class SomeDerived extends SomeBase {
|
||||
constructor() {
|
||||
super();
|
||||
this.x = 42;
|
||||
}
|
||||
}
|
||||
class SomeOther {
|
||||
constructor() {
|
||||
this.q = 42;
|
||||
}
|
||||
}
|
||||
|
||||
function SomeFakeClass() {
|
||||
/** @type {string|number} */
|
||||
this.p = "bar";
|
||||
}
|
||||
|
||||
// Type assertion should check for assignability in either direction
|
||||
var someBase = new SomeBase();
|
||||
var someDerived = new SomeDerived();
|
||||
var someOther = new SomeOther();
|
||||
var someFakeClass = new SomeFakeClass();
|
||||
|
||||
someBase = /** @type {SomeBase} */(someDerived);
|
||||
someBase = /** @type {SomeBase} */(someBase);
|
||||
someBase = /** @type {SomeBase} */(someOther); // Error
|
||||
~~~~~~~~~~~~~~~~
|
||||
!!! error TS2352: Type 'SomeOther' cannot be converted to type 'SomeBase'.
|
||||
!!! error TS2352: Property 'p' is missing in type 'SomeOther'.
|
||||
|
||||
someDerived = /** @type {SomeDerived} */(someDerived);
|
||||
someDerived = /** @type {SomeDerived} */(someBase);
|
||||
someDerived = /** @type {SomeDerived} */(someOther); // Error
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
!!! error TS2352: Type 'SomeOther' cannot be converted to type 'SomeDerived'.
|
||||
!!! error TS2352: Property 'x' is missing in type 'SomeOther'.
|
||||
|
||||
someOther = /** @type {SomeOther} */(someDerived); // Error
|
||||
~~~~~~~~~~~~~~~~~
|
||||
!!! error TS2352: Type 'SomeDerived' cannot be converted to type 'SomeOther'.
|
||||
!!! error TS2352: Property 'q' is missing in type 'SomeDerived'.
|
||||
someOther = /** @type {SomeOther} */(someBase); // Error
|
||||
~~~~~~~~~~~~~~~~~
|
||||
!!! error TS2352: Type 'SomeBase' cannot be converted to type 'SomeOther'.
|
||||
!!! error TS2352: Property 'q' is missing in type 'SomeBase'.
|
||||
someOther = /** @type {SomeOther} */(someOther);
|
||||
|
||||
someFakeClass = someBase;
|
||||
someFakeClass = someDerived;
|
||||
|
||||
someBase = someFakeClass; // Error
|
||||
~~~~~~~~
|
||||
!!! error TS2322: Type '{ p: string | number | undefined; }' is not assignable to type 'SomeBase'.
|
||||
!!! error TS2322: Types of property 'p' are incompatible.
|
||||
!!! error TS2322: Type 'string | number | undefined' is not assignable to type 'number'.
|
||||
!!! error TS2322: Type 'undefined' is not assignable to type 'number'.
|
||||
someBase = /** @type {SomeBase} */(someFakeClass);
|
||||
|
||||
// Type assertion cannot be a type-predicate type
|
||||
/** @type {number | string} */
|
||||
var numOrStr;
|
||||
/** @type {string} */
|
||||
var str;
|
||||
if(/** @type {numOrStr is string} */(numOrStr === undefined)) { // Error
|
||||
~~~~~~~~~~~~~~~
|
||||
!!! error TS2352: Type 'boolean' cannot be converted to type 'string | number'.
|
||||
~~~~~~~~
|
||||
!!! error TS2304: Cannot find name 'numOrStr'.
|
||||
~~
|
||||
!!! error TS1005: '}' expected.
|
||||
~~~~~~~~
|
||||
!!! error TS2454: Variable 'numOrStr' is used before being assigned.
|
||||
str = numOrStr; // Error, no narrowing occurred
|
||||
~~~
|
||||
!!! error TS2322: Type 'string | number' is not assignable to type 'string'.
|
||||
!!! error TS2322: Type 'number' is not assignable to type 'string'.
|
||||
~~~~~~~~
|
||||
!!! error TS2454: Variable 'numOrStr' is used before being assigned.
|
||||
}
|
||||
|
||||
|
||||
|
||||
151
tests/baselines/reference/jsdocTypeTagCast.js
Normal file
151
tests/baselines/reference/jsdocTypeTagCast.js
Normal file
@ -0,0 +1,151 @@
|
||||
//// [tests/cases/conformance/jsdoc/jsdocTypeTagCast.ts] ////
|
||||
|
||||
//// [a.ts]
|
||||
var W: string;
|
||||
|
||||
//// [b.js]
|
||||
// @ts-check
|
||||
var W = /** @type {string} */(/** @type {*} */ (4));
|
||||
|
||||
var W = /** @type {string} */(4); // Error
|
||||
|
||||
/** @type {*} */
|
||||
var a;
|
||||
|
||||
/** @type {string} */
|
||||
var s;
|
||||
|
||||
var a = /** @type {*} */("" + 4);
|
||||
var s = "" + /** @type {*} */(4);
|
||||
|
||||
class SomeBase {
|
||||
constructor() {
|
||||
this.p = 42;
|
||||
}
|
||||
}
|
||||
class SomeDerived extends SomeBase {
|
||||
constructor() {
|
||||
super();
|
||||
this.x = 42;
|
||||
}
|
||||
}
|
||||
class SomeOther {
|
||||
constructor() {
|
||||
this.q = 42;
|
||||
}
|
||||
}
|
||||
|
||||
function SomeFakeClass() {
|
||||
/** @type {string|number} */
|
||||
this.p = "bar";
|
||||
}
|
||||
|
||||
// Type assertion should check for assignability in either direction
|
||||
var someBase = new SomeBase();
|
||||
var someDerived = new SomeDerived();
|
||||
var someOther = new SomeOther();
|
||||
var someFakeClass = new SomeFakeClass();
|
||||
|
||||
someBase = /** @type {SomeBase} */(someDerived);
|
||||
someBase = /** @type {SomeBase} */(someBase);
|
||||
someBase = /** @type {SomeBase} */(someOther); // Error
|
||||
|
||||
someDerived = /** @type {SomeDerived} */(someDerived);
|
||||
someDerived = /** @type {SomeDerived} */(someBase);
|
||||
someDerived = /** @type {SomeDerived} */(someOther); // Error
|
||||
|
||||
someOther = /** @type {SomeOther} */(someDerived); // Error
|
||||
someOther = /** @type {SomeOther} */(someBase); // Error
|
||||
someOther = /** @type {SomeOther} */(someOther);
|
||||
|
||||
someFakeClass = someBase;
|
||||
someFakeClass = someDerived;
|
||||
|
||||
someBase = someFakeClass; // Error
|
||||
someBase = /** @type {SomeBase} */(someFakeClass);
|
||||
|
||||
// Type assertion cannot be a type-predicate type
|
||||
/** @type {number | string} */
|
||||
var numOrStr;
|
||||
/** @type {string} */
|
||||
var str;
|
||||
if(/** @type {numOrStr is string} */(numOrStr === undefined)) { // Error
|
||||
str = numOrStr; // Error, no narrowing occurred
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//// [a.js]
|
||||
var W;
|
||||
//// [b.js]
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
||||
return function (d, b) {
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
// @ts-check
|
||||
var W = ((4));
|
||||
var W = (4); // Error
|
||||
/** @type {*} */
|
||||
var a;
|
||||
/** @type {string} */
|
||||
var s;
|
||||
var a = ("" + 4);
|
||||
var s = "" + (4);
|
||||
var SomeBase = (function () {
|
||||
function SomeBase() {
|
||||
this.p = 42;
|
||||
}
|
||||
return SomeBase;
|
||||
}());
|
||||
var SomeDerived = (function (_super) {
|
||||
__extends(SomeDerived, _super);
|
||||
function SomeDerived() {
|
||||
var _this = _super.call(this) || this;
|
||||
_this.x = 42;
|
||||
return _this;
|
||||
}
|
||||
return SomeDerived;
|
||||
}(SomeBase));
|
||||
var SomeOther = (function () {
|
||||
function SomeOther() {
|
||||
this.q = 42;
|
||||
}
|
||||
return SomeOther;
|
||||
}());
|
||||
function SomeFakeClass() {
|
||||
/** @type {string|number} */
|
||||
this.p = "bar";
|
||||
}
|
||||
// Type assertion should check for assignability in either direction
|
||||
var someBase = new SomeBase();
|
||||
var someDerived = new SomeDerived();
|
||||
var someOther = new SomeOther();
|
||||
var someFakeClass = new SomeFakeClass();
|
||||
someBase = (someDerived);
|
||||
someBase = (someBase);
|
||||
someBase = (someOther); // Error
|
||||
someDerived = (someDerived);
|
||||
someDerived = (someBase);
|
||||
someDerived = (someOther); // Error
|
||||
someOther = (someDerived); // Error
|
||||
someOther = (someBase); // Error
|
||||
someOther = (someOther);
|
||||
someFakeClass = someBase;
|
||||
someFakeClass = someDerived;
|
||||
someBase = someFakeClass; // Error
|
||||
someBase = (someFakeClass);
|
||||
// Type assertion cannot be a type-predicate type
|
||||
/** @type {number | string} */
|
||||
var numOrStr;
|
||||
/** @type {string} */
|
||||
var str;
|
||||
if ((numOrStr === undefined)) {
|
||||
str = numOrStr; // Error, no narrowing occurred
|
||||
}
|
||||
78
tests/cases/conformance/jsdoc/jsdocTypeTagCast.ts
Normal file
78
tests/cases/conformance/jsdoc/jsdocTypeTagCast.ts
Normal file
@ -0,0 +1,78 @@
|
||||
// @allowJS: true
|
||||
// @suppressOutputPathCheck: true
|
||||
// @strictNullChecks: true
|
||||
|
||||
// @filename: a.ts
|
||||
var W: string;
|
||||
|
||||
// @filename: b.js
|
||||
// @ts-check
|
||||
var W = /** @type {string} */(/** @type {*} */ (4));
|
||||
|
||||
var W = /** @type {string} */(4); // Error
|
||||
|
||||
/** @type {*} */
|
||||
var a;
|
||||
|
||||
/** @type {string} */
|
||||
var s;
|
||||
|
||||
var a = /** @type {*} */("" + 4);
|
||||
var s = "" + /** @type {*} */(4);
|
||||
|
||||
class SomeBase {
|
||||
constructor() {
|
||||
this.p = 42;
|
||||
}
|
||||
}
|
||||
class SomeDerived extends SomeBase {
|
||||
constructor() {
|
||||
super();
|
||||
this.x = 42;
|
||||
}
|
||||
}
|
||||
class SomeOther {
|
||||
constructor() {
|
||||
this.q = 42;
|
||||
}
|
||||
}
|
||||
|
||||
function SomeFakeClass() {
|
||||
/** @type {string|number} */
|
||||
this.p = "bar";
|
||||
}
|
||||
|
||||
// Type assertion should check for assignability in either direction
|
||||
var someBase = new SomeBase();
|
||||
var someDerived = new SomeDerived();
|
||||
var someOther = new SomeOther();
|
||||
var someFakeClass = new SomeFakeClass();
|
||||
|
||||
someBase = /** @type {SomeBase} */(someDerived);
|
||||
someBase = /** @type {SomeBase} */(someBase);
|
||||
someBase = /** @type {SomeBase} */(someOther); // Error
|
||||
|
||||
someDerived = /** @type {SomeDerived} */(someDerived);
|
||||
someDerived = /** @type {SomeDerived} */(someBase);
|
||||
someDerived = /** @type {SomeDerived} */(someOther); // Error
|
||||
|
||||
someOther = /** @type {SomeOther} */(someDerived); // Error
|
||||
someOther = /** @type {SomeOther} */(someBase); // Error
|
||||
someOther = /** @type {SomeOther} */(someOther);
|
||||
|
||||
someFakeClass = someBase;
|
||||
someFakeClass = someDerived;
|
||||
|
||||
someBase = someFakeClass; // Error
|
||||
someBase = /** @type {SomeBase} */(someFakeClass);
|
||||
|
||||
// Type assertion cannot be a type-predicate type
|
||||
/** @type {number | string} */
|
||||
var numOrStr;
|
||||
/** @type {string} */
|
||||
var str;
|
||||
if(/** @type {numOrStr is string} */(numOrStr === undefined)) { // Error
|
||||
str = numOrStr; // Error, no narrowing occurred
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user