From 0222211acb0cefc0f9462aeaf61d29008c0c1a06 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 17 Mar 2020 19:20:56 -0700 Subject: [PATCH] Support for/of variables in assertion checking (#37432) * Support for/of variables in assertion checking * Integrate with "dotted name" logic * Add tests --- src/compiler/checker.ts | 47 +++--- .../assertionTypePredicates1.errors.txt | 52 +++++-- .../reference/assertionTypePredicates1.js | 54 +++++++ .../assertionTypePredicates1.symbols | 138 +++++++++++++----- .../reference/assertionTypePredicates1.types | 69 +++++++++ .../controlFlow/assertionTypePredicates1.ts | 28 ++++ 6 files changed, 325 insertions(+), 63 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 773be68dad..4fe8e264e5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7266,7 +7266,7 @@ namespace ts { // [Symbol.iterator] or next). This may be because we accessed properties from anyType, // or it may have led to an error inside getElementTypeOfIterable. const forOfStatement = declaration.parent.parent; - return checkRightHandSideOfForOf(forOfStatement.expression, forOfStatement.awaitModifier) || anyType; + return checkRightHandSideOfForOf(forOfStatement) || anyType; } if (isBindingPattern(declaration.parent)) { @@ -19204,7 +19204,7 @@ namespace ts { case SyntaxKind.ForInStatement: return stringType; case SyntaxKind.ForOfStatement: - return checkRightHandSideOfForOf((parent).expression, (parent).awaitModifier) || errorType; + return checkRightHandSideOfForOf(parent) || errorType; case SyntaxKind.BinaryExpression: return getAssignedTypeOfBinaryExpression(parent); case SyntaxKind.DeleteExpression: @@ -19248,7 +19248,7 @@ namespace ts { return stringType; } if (node.parent.parent.kind === SyntaxKind.ForOfStatement) { - return checkRightHandSideOfForOf(node.parent.parent.expression, node.parent.parent.awaitModifier) || errorType; + return checkRightHandSideOfForOf(node.parent.parent) || errorType; } return errorType; } @@ -19513,11 +19513,10 @@ namespace ts { return isLengthPushOrUnshift || isElementAssignment; } - function isDeclarationWithExplicitTypeAnnotation(declaration: Declaration | undefined) { - return !!(declaration && ( - declaration.kind === SyntaxKind.VariableDeclaration || declaration.kind === SyntaxKind.Parameter || + function isDeclarationWithExplicitTypeAnnotation(declaration: Declaration) { + return (declaration.kind === SyntaxKind.VariableDeclaration || declaration.kind === SyntaxKind.Parameter || declaration.kind === SyntaxKind.PropertyDeclaration || declaration.kind === SyntaxKind.PropertySignature) && - getEffectiveTypeAnnotationNode(declaration as VariableDeclaration | ParameterDeclaration | PropertyDeclaration | PropertySignature)); + !!getEffectiveTypeAnnotationNode(declaration as VariableDeclaration | ParameterDeclaration | PropertyDeclaration | PropertySignature); } function getExplicitTypeOfSymbol(symbol: Symbol, diagnostic?: Diagnostic) { @@ -19525,11 +19524,20 @@ namespace ts { return getTypeOfSymbol(symbol); } if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) { - if (isDeclarationWithExplicitTypeAnnotation(symbol.valueDeclaration)) { - return getTypeOfSymbol(symbol); - } - if (diagnostic && symbol.valueDeclaration) { - addRelatedInfo(diagnostic, createDiagnosticForNode(symbol.valueDeclaration, Diagnostics._0_needs_an_explicit_type_annotation, symbolToString(symbol))); + const declaration = symbol.valueDeclaration; + if (declaration) { + if (isDeclarationWithExplicitTypeAnnotation(declaration)) { + return getTypeOfSymbol(symbol); + } + if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === SyntaxKind.ForOfStatement) { + const expressionType = getTypeOfDottedName(declaration.parent.parent.expression, /*diagnostic*/ undefined); + if (expressionType) { + return getForOfIterationType(declaration.parent.parent, expressionType); + } + } + if (diagnostic) { + addRelatedInfo(diagnostic, createDiagnosticForNode(declaration, Diagnostics._0_needs_an_explicit_type_annotation, symbolToString(symbol))); + } } } } @@ -31585,7 +31593,7 @@ namespace ts { } else { const varExpr = node.initializer; - const iteratedType = checkRightHandSideOfForOf(node.expression, node.awaitModifier); + const iteratedType = checkRightHandSideOfForOf(node); // There may be a destructuring assignment on the left side if (varExpr.kind === SyntaxKind.ArrayLiteralExpression || varExpr.kind === SyntaxKind.ObjectLiteralExpression) { @@ -31677,10 +31685,13 @@ namespace ts { } } - function checkRightHandSideOfForOf(rhsExpression: Expression, awaitModifier: AwaitKeywordToken | undefined): Type { - const expressionType = checkNonNullExpression(rhsExpression); - const use = awaitModifier ? IterationUse.ForAwaitOf : IterationUse.ForOf; - return checkIteratedTypeOrElementType(use, expressionType, undefinedType, rhsExpression); + function checkRightHandSideOfForOf(statement: ForOfStatement): Type { + return getForOfIterationType(statement, checkNonNullExpression(statement.expression)); + } + + function getForOfIterationType(statement: ForOfStatement, expressionType: Type) { + const use = statement.awaitModifier ? IterationUse.ForAwaitOf : IterationUse.ForOf; + return checkIteratedTypeOrElementType(use, expressionType, undefinedType, statement.expression); } function checkIteratedTypeOrElementType(use: IterationUse, inputType: Type, sentType: Type, errorNode: Node | undefined): Type { @@ -35046,7 +35057,7 @@ namespace ts { // for ( { a } of elems) { // } if (expr.parent.kind === SyntaxKind.ForOfStatement) { - const iteratedType = checkRightHandSideOfForOf((expr.parent).expression, (expr.parent).awaitModifier); + const iteratedType = checkRightHandSideOfForOf(expr.parent); return checkDestructuringAssignment(expr, iteratedType || errorType); } // If this is from "for" initializer diff --git a/tests/baselines/reference/assertionTypePredicates1.errors.txt b/tests/baselines/reference/assertionTypePredicates1.errors.txt index 1ce19562cf..4555f7b68f 100644 --- a/tests/baselines/reference/assertionTypePredicates1.errors.txt +++ b/tests/baselines/reference/assertionTypePredicates1.errors.txt @@ -3,16 +3,16 @@ tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(43,9): error TS7 tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(87,9): error TS7027: Unreachable code detected. tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(122,9): error TS7027: Unreachable code detected. tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(137,9): error TS7027: Unreachable code detected. -tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(143,37): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(144,37): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(145,37): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(148,15): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(149,15): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(150,15): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(151,15): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(156,5): error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation. -tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(158,5): error TS2776: Assertions require the call target to be an identifier or qualified name. -tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(160,5): error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation. +tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(153,37): error TS1228: A type predicate is only allowed in return type position for functions and methods. +tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(154,37): error TS1228: A type predicate is only allowed in return type position for functions and methods. +tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(155,37): error TS1228: A type predicate is only allowed in return type position for functions and methods. +tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(158,15): error TS1228: A type predicate is only allowed in return type position for functions and methods. +tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(159,15): error TS1228: A type predicate is only allowed in return type position for functions and methods. +tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(160,15): error TS1228: A type predicate is only allowed in return type position for functions and methods. +tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(161,15): error TS1228: A type predicate is only allowed in return type position for functions and methods. +tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(166,5): error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation. +tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(168,5): error TS2776: Assertions require the call target to be an identifier or qualified name. +tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(170,5): error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation. ==== tests/cases/conformance/controlFlow/assertionTypePredicates1.ts (15 errors) ==== @@ -166,6 +166,16 @@ tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(160,5): error TS } } + function f11(items: Test[]) { + for (let item of items) { + if (item.isTest2()) { + item.z; + } + item.assertIsTest2(); + item.z; + } + } + // Invalid constructs declare let Q1: new (x: unknown) => x is string; @@ -198,7 +208,7 @@ tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(160,5): error TS assert(typeof x === "string"); // Error ~~~~~~ !!! error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation. -!!! related TS2782 tests/cases/conformance/controlFlow/assertionTypePredicates1.ts:155:11: 'assert' needs an explicit type annotation. +!!! related TS2782 tests/cases/conformance/controlFlow/assertionTypePredicates1.ts:165:11: 'assert' needs an explicit type annotation. const a = [assert]; a[0](typeof x === "string"); // Error ~~~~ @@ -207,8 +217,26 @@ tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(160,5): error TS t1.assert(typeof x === "string"); // Error ~~~~~~~~~ !!! error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation. -!!! related TS2782 tests/cases/conformance/controlFlow/assertionTypePredicates1.ts:159:11: 't1' needs an explicit type annotation. +!!! related TS2782 tests/cases/conformance/controlFlow/assertionTypePredicates1.ts:169:11: 't1' needs an explicit type annotation. const t2: Test = new Test(); t2.assert(typeof x === "string"); } + + // Repro from #35940 + + interface Thing { + good: boolean; + isGood(): asserts this is GoodThing; + } + + interface GoodThing { + good: true; + } + + function example1(things: Thing[]) { + for (let thing of things) { + thing.isGood(); + thing.good; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/assertionTypePredicates1.js b/tests/baselines/reference/assertionTypePredicates1.js index 46e88e61ad..a9128475c5 100644 --- a/tests/baselines/reference/assertionTypePredicates1.js +++ b/tests/baselines/reference/assertionTypePredicates1.js @@ -139,6 +139,16 @@ class Derived extends Test { } } +function f11(items: Test[]) { + for (let item of items) { + if (item.isTest2()) { + item.z; + } + item.assertIsTest2(); + item.z; + } +} + // Invalid constructs declare let Q1: new (x: unknown) => x is string; @@ -162,6 +172,24 @@ function f20(x: unknown) { const t2: Test = new Test(); t2.assert(typeof x === "string"); } + +// Repro from #35940 + +interface Thing { + good: boolean; + isGood(): asserts this is GoodThing; +} + +interface GoodThing { + good: true; +} + +function example1(things: Thing[]) { + for (let thing of things) { + thing.isGood(); + thing.good; + } +} //// [assertionTypePredicates1.js] @@ -319,6 +347,16 @@ var Derived = /** @class */ (function (_super) { }; return Derived; }(Test)); +function f11(items) { + for (var _i = 0, items_1 = items; _i < items_1.length; _i++) { + var item = items_1[_i]; + if (item.isTest2()) { + item.z; + } + item.assertIsTest2(); + item.z; + } +} function f20(x) { var assert = function (value) { }; assert(typeof x === "string"); // Error @@ -329,6 +367,13 @@ function f20(x) { var t2 = new Test(); t2.assert(typeof x === "string"); } +function example1(things) { + for (var _i = 0, things_1 = things; _i < things_1.length; _i++) { + var thing = things_1[_i]; + thing.isGood(); + thing.good; + } +} //// [assertionTypePredicates1.d.ts] @@ -362,6 +407,7 @@ declare class Derived extends Test { foo(x: unknown): void; baz(x: number): void; } +declare function f11(items: Test[]): void; declare let Q1: new (x: unknown) => x is string; declare let Q2: new (x: boolean) => asserts x; declare let Q3: new (x: unknown) => asserts x is string; @@ -372,3 +418,11 @@ declare class Wat { set p2(x: asserts this is string); } declare function f20(x: unknown): void; +interface Thing { + good: boolean; + isGood(): asserts this is GoodThing; +} +interface GoodThing { + good: true; +} +declare function example1(things: Thing[]): void; diff --git a/tests/baselines/reference/assertionTypePredicates1.symbols b/tests/baselines/reference/assertionTypePredicates1.symbols index 90c42678d1..de13f6395b 100644 --- a/tests/baselines/reference/assertionTypePredicates1.symbols +++ b/tests/baselines/reference/assertionTypePredicates1.symbols @@ -392,81 +392,153 @@ class Derived extends Test { } } +function f11(items: Test[]) { +>f11 : Symbol(f11, Decl(assertionTypePredicates1.ts, 138, 1)) +>items : Symbol(items, Decl(assertionTypePredicates1.ts, 140, 13)) +>Test : Symbol(Test, Decl(assertionTypePredicates1.ts, 88, 1)) + + for (let item of items) { +>item : Symbol(item, Decl(assertionTypePredicates1.ts, 141, 12)) +>items : Symbol(items, Decl(assertionTypePredicates1.ts, 140, 13)) + + if (item.isTest2()) { +>item.isTest2 : Symbol(Test.isTest2, Decl(assertionTypePredicates1.ts, 94, 5)) +>item : Symbol(item, Decl(assertionTypePredicates1.ts, 141, 12)) +>isTest2 : Symbol(Test.isTest2, Decl(assertionTypePredicates1.ts, 94, 5)) + + item.z; +>item.z : Symbol(Test2.z, Decl(assertionTypePredicates1.ts, 125, 26)) +>item : Symbol(item, Decl(assertionTypePredicates1.ts, 141, 12)) +>z : Symbol(Test2.z, Decl(assertionTypePredicates1.ts, 125, 26)) + } + item.assertIsTest2(); +>item.assertIsTest2 : Symbol(Test.assertIsTest2, Decl(assertionTypePredicates1.ts, 97, 5)) +>item : Symbol(item, Decl(assertionTypePredicates1.ts, 141, 12)) +>assertIsTest2 : Symbol(Test.assertIsTest2, Decl(assertionTypePredicates1.ts, 97, 5)) + + item.z; +>item.z : Symbol(Test2.z, Decl(assertionTypePredicates1.ts, 125, 26)) +>item : Symbol(item, Decl(assertionTypePredicates1.ts, 141, 12)) +>z : Symbol(Test2.z, Decl(assertionTypePredicates1.ts, 125, 26)) + } +} + // Invalid constructs declare let Q1: new (x: unknown) => x is string; ->Q1 : Symbol(Q1, Decl(assertionTypePredicates1.ts, 142, 11)) ->x : Symbol(x, Decl(assertionTypePredicates1.ts, 142, 21)) ->x : Symbol(x, Decl(assertionTypePredicates1.ts, 142, 21)) +>Q1 : Symbol(Q1, Decl(assertionTypePredicates1.ts, 152, 11)) +>x : Symbol(x, Decl(assertionTypePredicates1.ts, 152, 21)) +>x : Symbol(x, Decl(assertionTypePredicates1.ts, 152, 21)) declare let Q2: new (x: boolean) => asserts x; ->Q2 : Symbol(Q2, Decl(assertionTypePredicates1.ts, 143, 11)) ->x : Symbol(x, Decl(assertionTypePredicates1.ts, 143, 21)) ->x : Symbol(x, Decl(assertionTypePredicates1.ts, 143, 21)) +>Q2 : Symbol(Q2, Decl(assertionTypePredicates1.ts, 153, 11)) +>x : Symbol(x, Decl(assertionTypePredicates1.ts, 153, 21)) +>x : Symbol(x, Decl(assertionTypePredicates1.ts, 153, 21)) declare let Q3: new (x: unknown) => asserts x is string; ->Q3 : Symbol(Q3, Decl(assertionTypePredicates1.ts, 144, 11)) ->x : Symbol(x, Decl(assertionTypePredicates1.ts, 144, 21)) ->x : Symbol(x, Decl(assertionTypePredicates1.ts, 144, 21)) +>Q3 : Symbol(Q3, Decl(assertionTypePredicates1.ts, 154, 11)) +>x : Symbol(x, Decl(assertionTypePredicates1.ts, 154, 21)) +>x : Symbol(x, Decl(assertionTypePredicates1.ts, 154, 21)) declare class Wat { ->Wat : Symbol(Wat, Decl(assertionTypePredicates1.ts, 144, 56)) +>Wat : Symbol(Wat, Decl(assertionTypePredicates1.ts, 154, 56)) get p1(): this is string; ->p1 : Symbol(Wat.p1, Decl(assertionTypePredicates1.ts, 146, 19), Decl(assertionTypePredicates1.ts, 147, 29)) +>p1 : Symbol(Wat.p1, Decl(assertionTypePredicates1.ts, 156, 19), Decl(assertionTypePredicates1.ts, 157, 29)) set p1(x: this is string); ->p1 : Symbol(Wat.p1, Decl(assertionTypePredicates1.ts, 146, 19), Decl(assertionTypePredicates1.ts, 147, 29)) ->x : Symbol(x, Decl(assertionTypePredicates1.ts, 148, 11)) +>p1 : Symbol(Wat.p1, Decl(assertionTypePredicates1.ts, 156, 19), Decl(assertionTypePredicates1.ts, 157, 29)) +>x : Symbol(x, Decl(assertionTypePredicates1.ts, 158, 11)) get p2(): asserts this is string; ->p2 : Symbol(Wat.p2, Decl(assertionTypePredicates1.ts, 148, 30), Decl(assertionTypePredicates1.ts, 149, 37)) +>p2 : Symbol(Wat.p2, Decl(assertionTypePredicates1.ts, 158, 30), Decl(assertionTypePredicates1.ts, 159, 37)) set p2(x: asserts this is string); ->p2 : Symbol(Wat.p2, Decl(assertionTypePredicates1.ts, 148, 30), Decl(assertionTypePredicates1.ts, 149, 37)) ->x : Symbol(x, Decl(assertionTypePredicates1.ts, 150, 11)) +>p2 : Symbol(Wat.p2, Decl(assertionTypePredicates1.ts, 158, 30), Decl(assertionTypePredicates1.ts, 159, 37)) +>x : Symbol(x, Decl(assertionTypePredicates1.ts, 160, 11)) } function f20(x: unknown) { ->f20 : Symbol(f20, Decl(assertionTypePredicates1.ts, 151, 1)) ->x : Symbol(x, Decl(assertionTypePredicates1.ts, 153, 13)) +>f20 : Symbol(f20, Decl(assertionTypePredicates1.ts, 161, 1)) +>x : Symbol(x, Decl(assertionTypePredicates1.ts, 163, 13)) const assert = (value: unknown): asserts value => {} ->assert : Symbol(assert, Decl(assertionTypePredicates1.ts, 154, 9)) ->value : Symbol(value, Decl(assertionTypePredicates1.ts, 154, 20)) ->value : Symbol(value, Decl(assertionTypePredicates1.ts, 154, 20)) +>assert : Symbol(assert, Decl(assertionTypePredicates1.ts, 164, 9)) +>value : Symbol(value, Decl(assertionTypePredicates1.ts, 164, 20)) +>value : Symbol(value, Decl(assertionTypePredicates1.ts, 164, 20)) assert(typeof x === "string"); // Error ->assert : Symbol(assert, Decl(assertionTypePredicates1.ts, 154, 9)) ->x : Symbol(x, Decl(assertionTypePredicates1.ts, 153, 13)) +>assert : Symbol(assert, Decl(assertionTypePredicates1.ts, 164, 9)) +>x : Symbol(x, Decl(assertionTypePredicates1.ts, 163, 13)) const a = [assert]; ->a : Symbol(a, Decl(assertionTypePredicates1.ts, 156, 9)) ->assert : Symbol(assert, Decl(assertionTypePredicates1.ts, 154, 9)) +>a : Symbol(a, Decl(assertionTypePredicates1.ts, 166, 9)) +>assert : Symbol(assert, Decl(assertionTypePredicates1.ts, 164, 9)) a[0](typeof x === "string"); // Error ->a : Symbol(a, Decl(assertionTypePredicates1.ts, 156, 9)) ->x : Symbol(x, Decl(assertionTypePredicates1.ts, 153, 13)) +>a : Symbol(a, Decl(assertionTypePredicates1.ts, 166, 9)) +>x : Symbol(x, Decl(assertionTypePredicates1.ts, 163, 13)) const t1 = new Test(); ->t1 : Symbol(t1, Decl(assertionTypePredicates1.ts, 158, 9)) +>t1 : Symbol(t1, Decl(assertionTypePredicates1.ts, 168, 9)) >Test : Symbol(Test, Decl(assertionTypePredicates1.ts, 88, 1)) t1.assert(typeof x === "string"); // Error >t1.assert : Symbol(Test.assert, Decl(assertionTypePredicates1.ts, 90, 12)) ->t1 : Symbol(t1, Decl(assertionTypePredicates1.ts, 158, 9)) +>t1 : Symbol(t1, Decl(assertionTypePredicates1.ts, 168, 9)) >assert : Symbol(Test.assert, Decl(assertionTypePredicates1.ts, 90, 12)) ->x : Symbol(x, Decl(assertionTypePredicates1.ts, 153, 13)) +>x : Symbol(x, Decl(assertionTypePredicates1.ts, 163, 13)) const t2: Test = new Test(); ->t2 : Symbol(t2, Decl(assertionTypePredicates1.ts, 160, 9)) +>t2 : Symbol(t2, Decl(assertionTypePredicates1.ts, 170, 9)) >Test : Symbol(Test, Decl(assertionTypePredicates1.ts, 88, 1)) >Test : Symbol(Test, Decl(assertionTypePredicates1.ts, 88, 1)) t2.assert(typeof x === "string"); >t2.assert : Symbol(Test.assert, Decl(assertionTypePredicates1.ts, 90, 12)) ->t2 : Symbol(t2, Decl(assertionTypePredicates1.ts, 160, 9)) +>t2 : Symbol(t2, Decl(assertionTypePredicates1.ts, 170, 9)) >assert : Symbol(Test.assert, Decl(assertionTypePredicates1.ts, 90, 12)) ->x : Symbol(x, Decl(assertionTypePredicates1.ts, 153, 13)) +>x : Symbol(x, Decl(assertionTypePredicates1.ts, 163, 13)) +} + +// Repro from #35940 + +interface Thing { +>Thing : Symbol(Thing, Decl(assertionTypePredicates1.ts, 172, 1)) + + good: boolean; +>good : Symbol(Thing.good, Decl(assertionTypePredicates1.ts, 176, 17)) + + isGood(): asserts this is GoodThing; +>isGood : Symbol(Thing.isGood, Decl(assertionTypePredicates1.ts, 177, 18)) +>GoodThing : Symbol(GoodThing, Decl(assertionTypePredicates1.ts, 179, 1)) +} + +interface GoodThing { +>GoodThing : Symbol(GoodThing, Decl(assertionTypePredicates1.ts, 179, 1)) + + good: true; +>good : Symbol(GoodThing.good, Decl(assertionTypePredicates1.ts, 181, 21)) +} + +function example1(things: Thing[]) { +>example1 : Symbol(example1, Decl(assertionTypePredicates1.ts, 183, 1)) +>things : Symbol(things, Decl(assertionTypePredicates1.ts, 185, 18)) +>Thing : Symbol(Thing, Decl(assertionTypePredicates1.ts, 172, 1)) + + for (let thing of things) { +>thing : Symbol(thing, Decl(assertionTypePredicates1.ts, 186, 12)) +>things : Symbol(things, Decl(assertionTypePredicates1.ts, 185, 18)) + + thing.isGood(); +>thing.isGood : Symbol(Thing.isGood, Decl(assertionTypePredicates1.ts, 177, 18)) +>thing : Symbol(thing, Decl(assertionTypePredicates1.ts, 186, 12)) +>isGood : Symbol(Thing.isGood, Decl(assertionTypePredicates1.ts, 177, 18)) + + thing.good; +>thing.good : Symbol(good, Decl(assertionTypePredicates1.ts, 176, 17), Decl(assertionTypePredicates1.ts, 181, 21)) +>thing : Symbol(thing, Decl(assertionTypePredicates1.ts, 186, 12)) +>good : Symbol(good, Decl(assertionTypePredicates1.ts, 176, 17), Decl(assertionTypePredicates1.ts, 181, 21)) + } } diff --git a/tests/baselines/reference/assertionTypePredicates1.types b/tests/baselines/reference/assertionTypePredicates1.types index 53c10d9a88..911f90a4a9 100644 --- a/tests/baselines/reference/assertionTypePredicates1.types +++ b/tests/baselines/reference/assertionTypePredicates1.types @@ -502,6 +502,38 @@ class Derived extends Test { } } +function f11(items: Test[]) { +>f11 : (items: Test[]) => void +>items : Test[] + + for (let item of items) { +>item : Test +>items : Test[] + + if (item.isTest2()) { +>item.isTest2() : boolean +>item.isTest2 : () => this is Test2 +>item : Test +>isTest2 : () => this is Test2 + + item.z; +>item.z : number +>item : Test2 +>z : number + } + item.assertIsTest2(); +>item.assertIsTest2() : void +>item.assertIsTest2 : () => asserts this is Test2 +>item : Test +>assertIsTest2 : () => asserts this is Test2 + + item.z; +>item.z : number +>item : Test2 +>z : number + } +} + // Invalid constructs declare let Q1: new (x: unknown) => x is string; @@ -597,3 +629,40 @@ function f20(x: unknown) { >"string" : "string" } +// Repro from #35940 + +interface Thing { + good: boolean; +>good : boolean + + isGood(): asserts this is GoodThing; +>isGood : () => asserts this is GoodThing +} + +interface GoodThing { + good: true; +>good : true +>true : true +} + +function example1(things: Thing[]) { +>example1 : (things: Thing[]) => void +>things : Thing[] + + for (let thing of things) { +>thing : Thing +>things : Thing[] + + thing.isGood(); +>thing.isGood() : void +>thing.isGood : () => asserts this is GoodThing +>thing : Thing +>isGood : () => asserts this is GoodThing + + thing.good; +>thing.good : true +>thing : Thing & GoodThing +>good : true + } +} + diff --git a/tests/cases/conformance/controlFlow/assertionTypePredicates1.ts b/tests/cases/conformance/controlFlow/assertionTypePredicates1.ts index 6830046359..cc90be3d84 100644 --- a/tests/cases/conformance/controlFlow/assertionTypePredicates1.ts +++ b/tests/cases/conformance/controlFlow/assertionTypePredicates1.ts @@ -142,6 +142,16 @@ class Derived extends Test { } } +function f11(items: Test[]) { + for (let item of items) { + if (item.isTest2()) { + item.z; + } + item.assertIsTest2(); + item.z; + } +} + // Invalid constructs declare let Q1: new (x: unknown) => x is string; @@ -165,3 +175,21 @@ function f20(x: unknown) { const t2: Test = new Test(); t2.assert(typeof x === "string"); } + +// Repro from #35940 + +interface Thing { + good: boolean; + isGood(): asserts this is GoodThing; +} + +interface GoodThing { + good: true; +} + +function example1(things: Thing[]) { + for (let thing of things) { + thing.isGood(); + thing.good; + } +}