From b5ec445f42fd847e7b5cb9a5c3d8dd375b7a2679 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 14 Jul 2017 13:02:33 -0700 Subject: [PATCH 1/2] Allow jsdoc casts of parenthesized expressions --- src/compiler/checker.ts | 28 +++- src/compiler/parser.ts | 2 +- src/compiler/utilities.ts | 3 +- .../reference/jsdocTypeTagCast.errors.txt | 108 +++++++++++++++ tests/baselines/reference/jsdocTypeTagCast.js | 130 ++++++++++++++++++ .../conformance/jsdoc/jsdocTypeTagCast.ts | 66 +++++++++ 6 files changed, 330 insertions(+), 7 deletions(-) create mode 100644 tests/baselines/reference/jsdocTypeTagCast.errors.txt create mode 100644 tests/baselines/reference/jsdocTypeTagCast.js create mode 100644 tests/cases/conformance/jsdoc/jsdocTypeTagCast.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dafa75372e1bf..141928efb5073 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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,20 @@ namespace ts { return type; } + function checkParenthesizedExpression(node: ParenthesizedExpression, checkMode?: CheckMode): Type { + if (isInJavaScriptFile(node)) { + if (node.jsDoc) { + const typecasts = flatten(map(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 +17792,7 @@ namespace ts { case SyntaxKind.TaggedTemplateExpression: return checkTaggedTemplateExpression(node); case SyntaxKind.ParenthesizedExpression: - return checkExpression((node).expression, checkMode); + return checkParenthesizedExpression(node, checkMode); case SyntaxKind.ClassExpression: return checkClassExpression(node); case SyntaxKind.FunctionExpression: diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 20c9559a8f452..4613f49a749e0 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -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 { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index b7fabdda38103..ab0afb1282090 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -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 '/**/' diff --git a/tests/baselines/reference/jsdocTypeTagCast.errors.txt b/tests/baselines/reference/jsdocTypeTagCast.errors.txt new file mode 100644 index 0000000000000..07a7bc94bc7f0 --- /dev/null +++ b/tests/baselines/reference/jsdocTypeTagCast.errors.txt @@ -0,0 +1,108 @@ +tests/cases/conformance/jsdoc/b.js(4,13): error TS2352: Type 'number' cannot be converted to type 'string'. +tests/cases/conformance/jsdoc/b.js(39,16): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeBase'. + Property 'p' is missing in type 'SomeOther'. +tests/cases/conformance/jsdoc/b.js(43,19): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeDerived'. + Property 'x' is missing in type 'SomeOther'. +tests/cases/conformance/jsdoc/b.js(45,17): error TS2352: Type 'SomeDerived' cannot be converted to type 'SomeOther'. + Property 'q' is missing in type 'SomeDerived'. +tests/cases/conformance/jsdoc/b.js(46,17): error TS2352: Type 'SomeBase' cannot be converted to type 'SomeOther'. + Property 'q' is missing in type 'SomeBase'. +tests/cases/conformance/jsdoc/b.js(54,8): error TS2352: Type 'boolean' cannot be converted to type 'string | number'. +tests/cases/conformance/jsdoc/b.js(54,15): error TS2304: Cannot find name 'numOrStr'. +tests/cases/conformance/jsdoc/b.js(54,24): error TS1005: '}' expected. +tests/cases/conformance/jsdoc/b.js(54,38): error TS2454: Variable 'numOrStr' is used before being assigned. +tests/cases/conformance/jsdoc/b.js(55,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(55,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 (11 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; + } + } + + // Type assertion should check for assignability in either direction + var someBase = new SomeBase(); + var someDerived = new SomeDerived(); + var someOther = new SomeOther(); + + 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); + + // 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. + } + + + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocTypeTagCast.js b/tests/baselines/reference/jsdocTypeTagCast.js new file mode 100644 index 0000000000000..979ccfd704919 --- /dev/null +++ b/tests/baselines/reference/jsdocTypeTagCast.js @@ -0,0 +1,130 @@ +//// [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; + } +} + +// Type assertion should check for assignability in either direction +var someBase = new SomeBase(); +var someDerived = new SomeDerived(); +var someOther = new SomeOther(); + +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); + +// 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; +}()); +// Type assertion should check for assignability in either direction +var someBase = new SomeBase(); +var someDerived = new SomeDerived(); +var someOther = new SomeOther(); +someBase = (someDerived); +someBase = (someBase); +someBase = (someOther); // Error +someDerived = (someDerived); +someDerived = (someBase); +someDerived = (someOther); // Error +someOther = (someDerived); // Error +someOther = (someBase); // Error +someOther = (someOther); +// 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 +} diff --git a/tests/cases/conformance/jsdoc/jsdocTypeTagCast.ts b/tests/cases/conformance/jsdoc/jsdocTypeTagCast.ts new file mode 100644 index 0000000000000..62bc549cf2c84 --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocTypeTagCast.ts @@ -0,0 +1,66 @@ +// @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; + } +} + +// Type assertion should check for assignability in either direction +var someBase = new SomeBase(); +var someDerived = new SomeDerived(); +var someOther = new SomeOther(); + +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); + +// 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 +} + + From 51b183a0a1f5006e9f0606eb2da7d945f719f8c4 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 17 Jul 2017 15:44:58 -0700 Subject: [PATCH 2/2] Feedback from #17211 --- src/compiler/checker.ts | 14 +++--- .../reference/jsdocTypeTagCast.errors.txt | 43 ++++++++++++++----- tests/baselines/reference/jsdocTypeTagCast.js | 21 +++++++++ .../conformance/jsdoc/jsdocTypeTagCast.ts | 12 ++++++ 4 files changed, 71 insertions(+), 19 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 141928efb5073..be14b7d95c924 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17740,14 +17740,12 @@ namespace ts { } function checkParenthesizedExpression(node: ParenthesizedExpression, checkMode?: CheckMode): Type { - if (isInJavaScriptFile(node)) { - if (node.jsDoc) { - const typecasts = flatten(map(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); - } + 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); diff --git a/tests/baselines/reference/jsdocTypeTagCast.errors.txt b/tests/baselines/reference/jsdocTypeTagCast.errors.txt index 07a7bc94bc7f0..f1f53ec1ecbe6 100644 --- a/tests/baselines/reference/jsdocTypeTagCast.errors.txt +++ b/tests/baselines/reference/jsdocTypeTagCast.errors.txt @@ -1,25 +1,29 @@ tests/cases/conformance/jsdoc/b.js(4,13): error TS2352: Type 'number' cannot be converted to type 'string'. -tests/cases/conformance/jsdoc/b.js(39,16): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeBase'. +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(43,19): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeDerived'. +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(45,17): error TS2352: Type 'SomeDerived' cannot be converted to 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(46,17): error TS2352: Type 'SomeBase' cannot be converted to type 'SomeOther'. +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(54,8): error TS2352: Type 'boolean' cannot be converted to type 'string | number'. -tests/cases/conformance/jsdoc/b.js(54,15): error TS2304: Cannot find name 'numOrStr'. -tests/cases/conformance/jsdoc/b.js(54,24): error TS1005: '}' expected. -tests/cases/conformance/jsdoc/b.js(54,38): error TS2454: Variable 'numOrStr' is used before being assigned. -tests/cases/conformance/jsdoc/b.js(55,2): error TS2322: Type 'string | number' is not assignable to type 'string'. +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(55,8): error TS2454: Variable 'numOrStr' is used before being assigned. +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 (11 errors) ==== +==== tests/cases/conformance/jsdoc/b.js (12 errors) ==== // @ts-check var W = /** @type {string} */(/** @type {*} */ (4)); @@ -53,10 +57,16 @@ tests/cases/conformance/jsdoc/b.js(55,8): error TS2454: Variable 'numOrStr' is u } } + 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); @@ -82,6 +92,17 @@ tests/cases/conformance/jsdoc/b.js(55,8): error TS2454: Variable 'numOrStr' is u !!! 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; diff --git a/tests/baselines/reference/jsdocTypeTagCast.js b/tests/baselines/reference/jsdocTypeTagCast.js index 979ccfd704919..c2df50cd6dd4a 100644 --- a/tests/baselines/reference/jsdocTypeTagCast.js +++ b/tests/baselines/reference/jsdocTypeTagCast.js @@ -35,10 +35,16 @@ class 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 = /** @type {SomeBase} */(someDerived); someBase = /** @type {SomeBase} */(someBase); @@ -52,6 +58,12 @@ 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; @@ -107,10 +119,15 @@ var SomeOther = (function () { } 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 @@ -120,6 +137,10 @@ 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; diff --git a/tests/cases/conformance/jsdoc/jsdocTypeTagCast.ts b/tests/cases/conformance/jsdoc/jsdocTypeTagCast.ts index 62bc549cf2c84..60ca43b053346 100644 --- a/tests/cases/conformance/jsdoc/jsdocTypeTagCast.ts +++ b/tests/cases/conformance/jsdoc/jsdocTypeTagCast.ts @@ -37,10 +37,16 @@ class 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 = /** @type {SomeBase} */(someDerived); someBase = /** @type {SomeBase} */(someBase); @@ -54,6 +60,12 @@ 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;