From 343f3df34e90371b318d6ce7bd968856abeacf02 Mon Sep 17 00:00:00 2001 From: Jack Williams Date: Wed, 17 Jul 2019 07:42:11 -0700 Subject: [PATCH 1/3] Remove assignability cases in getNarrowedType --- src/compiler/checker.ts | 10 ++-------- .../reference/controlFlowInstanceof.symbols | 4 ++-- .../reference/controlFlowInstanceof.types | 14 +++++++------- .../reference/instanceOfAssignability.types | 8 ++++---- .../reference/instanceofWithPrimitiveUnion.types | 4 ++-- .../narrowingConstrainedTypeParameter.types | 2 +- .../baselines/reference/typeGuardFunction.types | 2 +- .../reference/typeGuardIntersectionTypes.symbols | 16 ++++++++-------- .../reference/typeGuardIntersectionTypes.types | 10 +++++----- ...thInstanceOfByConstructorSignature.errors.txt | 12 +----------- ...rdsWithInstanceOfByConstructorSignature.types | 4 ++-- 11 files changed, 35 insertions(+), 51 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 20a4246f37253..990f7c01f7df0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17551,14 +17551,8 @@ namespace ts { } } // If the candidate type is a subtype of the target type, narrow to the candidate type. - // Otherwise, if the target type is assignable to the candidate type, keep the target type. - // Otherwise, if the candidate type is assignable to the target type, narrow to the candidate - // type. Otherwise, the types are completely unrelated, so narrow to an intersection of the - // two types. - return isTypeSubtypeOf(candidate, type) ? candidate : - isTypeAssignableTo(type, candidate) ? type : - isTypeAssignableTo(candidate, type) ? candidate : - getIntersectionType([type, candidate]); + // Otherwise, narrow to an intersection of the two types. + return isTypeSubtypeOf(candidate, type) ? candidate : getIntersectionType([type, candidate]); } function narrowTypeByTypePredicate(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type { diff --git a/tests/baselines/reference/controlFlowInstanceof.symbols b/tests/baselines/reference/controlFlowInstanceof.symbols index 2be43d5ef6eee..350e8d1830ae6 100644 --- a/tests/baselines/reference/controlFlowInstanceof.symbols +++ b/tests/baselines/reference/controlFlowInstanceof.symbols @@ -25,9 +25,9 @@ function f1(s: Set | Set) { >s : Symbol(s, Decl(controlFlowInstanceof.ts, 2, 12)) s.add(42); ->s.add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --)) +>s.add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) >s : Symbol(s, Decl(controlFlowInstanceof.ts, 2, 12)) ->add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --)) +>add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) } function f2(s: Set | Set) { diff --git a/tests/baselines/reference/controlFlowInstanceof.types b/tests/baselines/reference/controlFlowInstanceof.types index 9aa38025a1cab..16fb9bf634676 100644 --- a/tests/baselines/reference/controlFlowInstanceof.types +++ b/tests/baselines/reference/controlFlowInstanceof.types @@ -20,16 +20,16 @@ function f1(s: Set | Set) { >Set : SetConstructor s; // Set ->s : Set +>s : Set & Set } s; // Set ->s : Set +>s : Set & Set s.add(42); ->s.add(42) : Set ->s.add : (value: number) => Set ->s : Set ->add : (value: number) => Set +>s.add(42) : Set & Set +>s.add : ((value: number) => Set & Set) & ((value: any) => Set & Set) +>s : Set & Set +>add : ((value: number) => Set & Set) & ((value: any) => Set & Set) >42 : 42 } @@ -105,7 +105,7 @@ function f4(s: Set | Set) { >Set : SetConstructor s; // Set ->s : Set +>s : Set & Set } else { s; // never diff --git a/tests/baselines/reference/instanceOfAssignability.types b/tests/baselines/reference/instanceOfAssignability.types index 7ce2209e9492e..85d15c944d1cc 100644 --- a/tests/baselines/reference/instanceOfAssignability.types +++ b/tests/baselines/reference/instanceOfAssignability.types @@ -70,8 +70,8 @@ function fn2(x: Base) { // 1.5: y: Base // Want: y: Derived1 let y = x; ->y : Derived1 ->x : Derived1 +>y : Base & Derived1 +>x : Base & Derived1 } } @@ -104,8 +104,8 @@ function fn4(x: Base|Derived2) { // 1.5: y: {} // Want: Derived1 let y = x; ->y : Derived1 ->x : Derived1 +>y : (Base & Derived1) | (Derived2 & Derived1) +>x : (Base & Derived1) | (Derived2 & Derived1) } } diff --git a/tests/baselines/reference/instanceofWithPrimitiveUnion.types b/tests/baselines/reference/instanceofWithPrimitiveUnion.types index fcaba05b51f1d..a3f5f3c3d753e 100644 --- a/tests/baselines/reference/instanceofWithPrimitiveUnion.types +++ b/tests/baselines/reference/instanceofWithPrimitiveUnion.types @@ -9,7 +9,7 @@ function test1(x: number | string) { >Object : ObjectConstructor x; ->x : string | number +>x : (string & Object) | (number & Object) } } @@ -23,7 +23,7 @@ function test2(x: (number | string) | number) { >Object : ObjectConstructor x; ->x : string | number +>x : (string & Object) | (number & Object) } } diff --git a/tests/baselines/reference/narrowingConstrainedTypeParameter.types b/tests/baselines/reference/narrowingConstrainedTypeParameter.types index 1fadbfdb1c48b..b8fc85bb10484 100644 --- a/tests/baselines/reference/narrowingConstrainedTypeParameter.types +++ b/tests/baselines/reference/narrowingConstrainedTypeParameter.types @@ -39,5 +39,5 @@ export function speak(pet: TPet, voice: (pet: TPet) => string) return voice(pet); >voice(pet) : string >voice : (pet: TPet) => string ->pet : TPet +>pet : TPet & Pet } diff --git a/tests/baselines/reference/typeGuardFunction.types b/tests/baselines/reference/typeGuardFunction.types index 6efe326dc9879..79554aaf14f2b 100644 --- a/tests/baselines/reference/typeGuardFunction.types +++ b/tests/baselines/reference/typeGuardFunction.types @@ -65,7 +65,7 @@ if(isA(subType)) { subType.propC; >subType.propC : number ->subType : C +>subType : C & A >propC : number } diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.symbols b/tests/baselines/reference/typeGuardIntersectionTypes.symbols index 21da29e434fec..f99148a9f0500 100644 --- a/tests/baselines/reference/typeGuardIntersectionTypes.symbols +++ b/tests/baselines/reference/typeGuardIntersectionTypes.symbols @@ -176,17 +176,17 @@ function identifyBeast(beast: Beast) { >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 64, 23)) if (beast.legs === 4) { ->beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>beast.legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 64, 23)) ->legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) log(`pegasus - 4 legs, wings`); >log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 48, 1)) } else if (beast.legs === 2) { ->beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>beast.legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 64, 23)) ->legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) log(`bird - 2 legs, wings`); >log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 48, 1)) @@ -194,9 +194,9 @@ function identifyBeast(beast: Beast) { else { log(`unknown - ${beast.legs} legs, wings`); >log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 48, 1)) ->beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>beast.legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 64, 23)) ->legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) } } @@ -204,9 +204,9 @@ function identifyBeast(beast: Beast) { else { log(`manbearpig - ${beast.legs} legs, no wings`); >log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 48, 1)) ->beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>beast.legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 64, 23)) ->legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) } } diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.types b/tests/baselines/reference/typeGuardIntersectionTypes.types index a9f43b4917930..bf119d5743c6e 100644 --- a/tests/baselines/reference/typeGuardIntersectionTypes.types +++ b/tests/baselines/reference/typeGuardIntersectionTypes.types @@ -166,12 +166,12 @@ function identifyBeast(beast: Beast) { if (hasWings(beast)) { >hasWings(beast) : boolean >hasWings : (x: Beast) => x is Winged ->beast : Legged +>beast : Beast & Legged if (beast.legs === 4) { >beast.legs === 4 : boolean >beast.legs : number ->beast : Legged & Winged +>beast : Beast & Legged & Winged >legs : number >4 : 4 @@ -183,7 +183,7 @@ function identifyBeast(beast: Beast) { else if (beast.legs === 2) { >beast.legs === 2 : boolean >beast.legs : number ->beast : Legged & Winged +>beast : Beast & Legged & Winged >legs : number >2 : 2 @@ -198,7 +198,7 @@ function identifyBeast(beast: Beast) { >log : (s: string) => void >`unknown - ${beast.legs} legs, wings` : string >beast.legs : number ->beast : Legged & Winged +>beast : Beast & Legged & Winged >legs : number } } @@ -210,7 +210,7 @@ function identifyBeast(beast: Beast) { >log : (s: string) => void >`manbearpig - ${beast.legs} legs, no wings` : string >beast.legs : number ->beast : Legged +>beast : Beast & Legged >legs : number } } diff --git a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt index a15eed1a8683e..5da6b9c2d188a 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt +++ b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt @@ -15,10 +15,6 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru Property 'bar1' does not exist on type 'E2'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(119,11): error TS2339: Property 'bar2' does not exist on type 'E1 | E2'. Property 'bar2' does not exist on type 'E1'. -tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(134,11): error TS2339: Property 'foo' does not exist on type 'string | F'. - Property 'foo' does not exist on type 'string'. -tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(135,11): error TS2339: Property 'bar' does not exist on type 'string | F'. - Property 'bar' does not exist on type 'string'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(160,11): error TS2339: Property 'foo2' does not exist on type 'G1'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(166,11): error TS2339: Property 'foo2' does not exist on type 'G1'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(182,11): error TS2339: Property 'bar' does not exist on type 'H'. @@ -26,7 +22,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(188,11): error TS2551: Property 'foo2' does not exist on type 'H'. Did you mean 'foo'? -==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts (20 errors) ==== +==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts (18 errors) ==== interface AConstructor { new (): A; } @@ -191,13 +187,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru var obj11: F | string; if (obj11 instanceof F) { // can't type narrowing, construct signature returns any. obj11.foo; - ~~~ -!!! error TS2339: Property 'foo' does not exist on type 'string | F'. -!!! error TS2339: Property 'foo' does not exist on type 'string'. obj11.bar; - ~~~ -!!! error TS2339: Property 'bar' does not exist on type 'string | F'. -!!! error TS2339: Property 'bar' does not exist on type 'string'. } var obj12: any; diff --git a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.types b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.types index 481abe9e59ca8..c3d8e81498274 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.types +++ b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.types @@ -341,12 +341,12 @@ if (obj11 instanceof F) { // can't type narrowing, construct signature returns a obj11.foo; >obj11.foo : any ->obj11 : string | F +>obj11 : any >foo : any obj11.bar; >obj11.bar : any ->obj11 : string | F +>obj11 : any >bar : any } From 798405c962f6c84c7f3cb674c0e0c253db55a422 Mon Sep 17 00:00:00 2001 From: Jack Williams Date: Wed, 17 Jul 2019 10:59:50 -0700 Subject: [PATCH 2/3] Fix linting --- src/compiler/emitter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index a1207c9b892c9..ea06ec712685d 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -411,7 +411,7 @@ namespace ts { } ); if (emitOnlyDtsFiles && declarationTransform.transformed[0].kind === SyntaxKind.SourceFile) { - const sourceFile = declarationTransform.transformed[0] as SourceFile; + const sourceFile = declarationTransform.transformed[0]; exportedModulesFromDeclarationEmit = sourceFile.exportedModulesFromDeclarationEmit; } } From b6026c20ea5aa358e918e979a38ad5727d59d420 Mon Sep 17 00:00:00 2001 From: Jack Williams Date: Sun, 18 Aug 2019 17:02:01 +0100 Subject: [PATCH 3/3] Add subtype case --- src/compiler/checker.ts | 7 +- .../reference/controlFlowInstanceof.symbols | 4 +- .../reference/controlFlowInstanceof.types | 14 ++-- .../instanceofWithPrimitiveUnion.types | 4 +- .../narrowingConstrainedTypeParameter.types | 2 +- .../reference/typeGuardFunction.types | 2 +- .../reference/typeGuardsWithInstanceOf.js | 51 ++++++++++++++- .../typeGuardsWithInstanceOf.symbols | 64 ++++++++++++++++++- .../reference/typeGuardsWithInstanceOf.types | 62 +++++++++++++++++- ...nstanceOfByConstructorSignature.errors.txt | 12 +++- ...WithInstanceOfByConstructorSignature.types | 4 +- .../typeGuards/typeGuardsWithInstanceOf.ts | 36 +++++++++-- 12 files changed, 232 insertions(+), 30 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1b09c5cdaa060..4317a12f984fe 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17652,9 +17652,10 @@ namespace ts { return assignableType; } } - // If the candidate type is a subtype of the target type, narrow to the candidate type. - // Otherwise, narrow to an intersection of the two types. - return isTypeSubtypeOf(candidate, type) ? candidate : getIntersectionType([type, candidate]); + // If the candidate type is a subtype of the target type, narrow to the candidate type, + // if the target type is a subtype of the candidate type, narrow to the target type, + // otherwise, narrow to an intersection of the two types. + return isTypeSubtypeOf(candidate, type) ? candidate : isTypeSubtypeOf(type, candidate) ? type : getIntersectionType([type, candidate]); } function narrowTypeByTypePredicate(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type { diff --git a/tests/baselines/reference/controlFlowInstanceof.symbols b/tests/baselines/reference/controlFlowInstanceof.symbols index 350e8d1830ae6..2be43d5ef6eee 100644 --- a/tests/baselines/reference/controlFlowInstanceof.symbols +++ b/tests/baselines/reference/controlFlowInstanceof.symbols @@ -25,9 +25,9 @@ function f1(s: Set | Set) { >s : Symbol(s, Decl(controlFlowInstanceof.ts, 2, 12)) s.add(42); ->s.add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) +>s.add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --)) >s : Symbol(s, Decl(controlFlowInstanceof.ts, 2, 12)) ->add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) +>add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --)) } function f2(s: Set | Set) { diff --git a/tests/baselines/reference/controlFlowInstanceof.types b/tests/baselines/reference/controlFlowInstanceof.types index 16fb9bf634676..9aa38025a1cab 100644 --- a/tests/baselines/reference/controlFlowInstanceof.types +++ b/tests/baselines/reference/controlFlowInstanceof.types @@ -20,16 +20,16 @@ function f1(s: Set | Set) { >Set : SetConstructor s; // Set ->s : Set & Set +>s : Set } s; // Set ->s : Set & Set +>s : Set s.add(42); ->s.add(42) : Set & Set ->s.add : ((value: number) => Set & Set) & ((value: any) => Set & Set) ->s : Set & Set ->add : ((value: number) => Set & Set) & ((value: any) => Set & Set) +>s.add(42) : Set +>s.add : (value: number) => Set +>s : Set +>add : (value: number) => Set >42 : 42 } @@ -105,7 +105,7 @@ function f4(s: Set | Set) { >Set : SetConstructor s; // Set ->s : Set & Set +>s : Set } else { s; // never diff --git a/tests/baselines/reference/instanceofWithPrimitiveUnion.types b/tests/baselines/reference/instanceofWithPrimitiveUnion.types index a3f5f3c3d753e..fcaba05b51f1d 100644 --- a/tests/baselines/reference/instanceofWithPrimitiveUnion.types +++ b/tests/baselines/reference/instanceofWithPrimitiveUnion.types @@ -9,7 +9,7 @@ function test1(x: number | string) { >Object : ObjectConstructor x; ->x : (string & Object) | (number & Object) +>x : string | number } } @@ -23,7 +23,7 @@ function test2(x: (number | string) | number) { >Object : ObjectConstructor x; ->x : (string & Object) | (number & Object) +>x : string | number } } diff --git a/tests/baselines/reference/narrowingConstrainedTypeParameter.types b/tests/baselines/reference/narrowingConstrainedTypeParameter.types index b8fc85bb10484..1fadbfdb1c48b 100644 --- a/tests/baselines/reference/narrowingConstrainedTypeParameter.types +++ b/tests/baselines/reference/narrowingConstrainedTypeParameter.types @@ -39,5 +39,5 @@ export function speak(pet: TPet, voice: (pet: TPet) => string) return voice(pet); >voice(pet) : string >voice : (pet: TPet) => string ->pet : TPet & Pet +>pet : TPet } diff --git a/tests/baselines/reference/typeGuardFunction.types b/tests/baselines/reference/typeGuardFunction.types index 79554aaf14f2b..6efe326dc9879 100644 --- a/tests/baselines/reference/typeGuardFunction.types +++ b/tests/baselines/reference/typeGuardFunction.types @@ -65,7 +65,7 @@ if(isA(subType)) { subType.propC; >subType.propC : number ->subType : C & A +>subType : C >propC : number } diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.js b/tests/baselines/reference/typeGuardsWithInstanceOf.js index 34af7037f0bd9..1f6b37e7c01ea 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOf.js +++ b/tests/baselines/reference/typeGuardsWithInstanceOf.js @@ -1,12 +1,39 @@ //// [typeGuardsWithInstanceOf.ts] interface I { global: string; } -var result: I; -var result2: I; +var result!: I; +var result2!: I; if (!(result instanceof RegExp)) { result = result2; } else if (!result.global) { -} +} + +// Repro from #31155 + +interface OnChanges { + onChanges(changes: Record): void +} +interface Validator { + validate(): null | Record; +} + +class C { + validate() { + return {} + } +} + +function foo() { + let v: Validator & Partial = null as any; + if (v instanceof C) { + v // Validator & Partial & C + } + v // Validator & Partial via subtype reduction + if (v.onChanges) { + v.onChanges({}); + } +} + //// [typeGuardsWithInstanceOf.js] var result; @@ -16,3 +43,21 @@ if (!(result instanceof RegExp)) { } else if (!result.global) { } +var C = /** @class */ (function () { + function C() { + } + C.prototype.validate = function () { + return {}; + }; + return C; +}()); +function foo() { + var v = null; + if (v instanceof C) { + v; // Validator & Partial & C + } + v; // Validator & Partial via subtype reduction + if (v.onChanges) { + v.onChanges({}); + } +} diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.symbols b/tests/baselines/reference/typeGuardsWithInstanceOf.symbols index 90be23463b9bf..7433250dc0a4c 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOf.symbols +++ b/tests/baselines/reference/typeGuardsWithInstanceOf.symbols @@ -3,11 +3,11 @@ interface I { global: string; } >I : Symbol(I, Decl(typeGuardsWithInstanceOf.ts, 0, 0)) >global : Symbol(I.global, Decl(typeGuardsWithInstanceOf.ts, 0, 13)) -var result: I; +var result!: I; >result : Symbol(result, Decl(typeGuardsWithInstanceOf.ts, 1, 3)) >I : Symbol(I, Decl(typeGuardsWithInstanceOf.ts, 0, 0)) -var result2: I; +var result2!: I; >result2 : Symbol(result2, Decl(typeGuardsWithInstanceOf.ts, 2, 3)) >I : Symbol(I, Decl(typeGuardsWithInstanceOf.ts, 0, 0)) @@ -24,3 +24,63 @@ if (!(result instanceof RegExp)) { >result : Symbol(result, Decl(typeGuardsWithInstanceOf.ts, 1, 3)) >global : Symbol(global, Decl(typeGuardsWithInstanceOf.ts, 0, 13), Decl(lib.es5.d.ts, --, --)) } + +// Repro from #31155 + +interface OnChanges { +>OnChanges : Symbol(OnChanges, Decl(typeGuardsWithInstanceOf.ts, 7, 1)) + + onChanges(changes: Record): void +>onChanges : Symbol(OnChanges.onChanges, Decl(typeGuardsWithInstanceOf.ts, 11, 21)) +>changes : Symbol(changes, Decl(typeGuardsWithInstanceOf.ts, 12, 14)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +} +interface Validator { +>Validator : Symbol(Validator, Decl(typeGuardsWithInstanceOf.ts, 13, 1)) + + validate(): null | Record; +>validate : Symbol(Validator.validate, Decl(typeGuardsWithInstanceOf.ts, 14, 21)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +} + +class C { +>C : Symbol(C, Decl(typeGuardsWithInstanceOf.ts, 16, 1)) + + validate() { +>validate : Symbol(C.validate, Decl(typeGuardsWithInstanceOf.ts, 18, 9)) + + return {} + } +} + +function foo() { +>foo : Symbol(foo, Decl(typeGuardsWithInstanceOf.ts, 22, 1)) + + let v: Validator & Partial = null as any; +>v : Symbol(v, Decl(typeGuardsWithInstanceOf.ts, 25, 7)) +>Validator : Symbol(Validator, Decl(typeGuardsWithInstanceOf.ts, 13, 1)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>OnChanges : Symbol(OnChanges, Decl(typeGuardsWithInstanceOf.ts, 7, 1)) + + if (v instanceof C) { +>v : Symbol(v, Decl(typeGuardsWithInstanceOf.ts, 25, 7)) +>C : Symbol(C, Decl(typeGuardsWithInstanceOf.ts, 16, 1)) + + v // Validator & Partial & C +>v : Symbol(v, Decl(typeGuardsWithInstanceOf.ts, 25, 7)) + } + v // Validator & Partial via subtype reduction +>v : Symbol(v, Decl(typeGuardsWithInstanceOf.ts, 25, 7)) + + if (v.onChanges) { +>v.onChanges : Symbol(onChanges, Decl(typeGuardsWithInstanceOf.ts, 11, 21)) +>v : Symbol(v, Decl(typeGuardsWithInstanceOf.ts, 25, 7)) +>onChanges : Symbol(onChanges, Decl(typeGuardsWithInstanceOf.ts, 11, 21)) + + v.onChanges({}); +>v.onChanges : Symbol(onChanges, Decl(typeGuardsWithInstanceOf.ts, 11, 21)) +>v : Symbol(v, Decl(typeGuardsWithInstanceOf.ts, 25, 7)) +>onChanges : Symbol(onChanges, Decl(typeGuardsWithInstanceOf.ts, 11, 21)) + } +} + diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.types b/tests/baselines/reference/typeGuardsWithInstanceOf.types index fa425123812af..c7a8a1c9ac76e 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOf.types +++ b/tests/baselines/reference/typeGuardsWithInstanceOf.types @@ -2,10 +2,10 @@ interface I { global: string; } >global : string -var result: I; +var result!: I; >result : I -var result2: I; +var result2!: I; >result2 : I if (!(result instanceof RegExp)) { @@ -26,3 +26,61 @@ if (!(result instanceof RegExp)) { >result : I & RegExp >global : never } + +// Repro from #31155 + +interface OnChanges { + onChanges(changes: Record): void +>onChanges : (changes: Record) => void +>changes : Record +} +interface Validator { + validate(): null | Record; +>validate : () => Record | null +>null : null +} + +class C { +>C : C + + validate() { +>validate : () => {} + + return {} +>{} : {} + } +} + +function foo() { +>foo : () => void + + let v: Validator & Partial = null as any; +>v : Validator & Partial +>null as any : any +>null : null + + if (v instanceof C) { +>v instanceof C : boolean +>v : Validator & Partial +>C : typeof C + + v // Validator & Partial & C +>v : Validator & Partial & C + } + v // Validator & Partial via subtype reduction +>v : Validator & Partial + + if (v.onChanges) { +>v.onChanges : ((changes: Record) => void) | undefined +>v : Validator & Partial +>onChanges : ((changes: Record) => void) | undefined + + v.onChanges({}); +>v.onChanges({}) : void +>v.onChanges : (changes: Record) => void +>v : Validator & Partial +>onChanges : (changes: Record) => void +>{} : {} + } +} + diff --git a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt index 5da6b9c2d188a..a15eed1a8683e 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt +++ b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt @@ -15,6 +15,10 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru Property 'bar1' does not exist on type 'E2'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(119,11): error TS2339: Property 'bar2' does not exist on type 'E1 | E2'. Property 'bar2' does not exist on type 'E1'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(134,11): error TS2339: Property 'foo' does not exist on type 'string | F'. + Property 'foo' does not exist on type 'string'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(135,11): error TS2339: Property 'bar' does not exist on type 'string | F'. + Property 'bar' does not exist on type 'string'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(160,11): error TS2339: Property 'foo2' does not exist on type 'G1'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(166,11): error TS2339: Property 'foo2' does not exist on type 'G1'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(182,11): error TS2339: Property 'bar' does not exist on type 'H'. @@ -22,7 +26,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(188,11): error TS2551: Property 'foo2' does not exist on type 'H'. Did you mean 'foo'? -==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts (18 errors) ==== +==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts (20 errors) ==== interface AConstructor { new (): A; } @@ -187,7 +191,13 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru var obj11: F | string; if (obj11 instanceof F) { // can't type narrowing, construct signature returns any. obj11.foo; + ~~~ +!!! error TS2339: Property 'foo' does not exist on type 'string | F'. +!!! error TS2339: Property 'foo' does not exist on type 'string'. obj11.bar; + ~~~ +!!! error TS2339: Property 'bar' does not exist on type 'string | F'. +!!! error TS2339: Property 'bar' does not exist on type 'string'. } var obj12: any; diff --git a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.types b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.types index c3d8e81498274..481abe9e59ca8 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.types +++ b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.types @@ -341,12 +341,12 @@ if (obj11 instanceof F) { // can't type narrowing, construct signature returns a obj11.foo; >obj11.foo : any ->obj11 : any +>obj11 : string | F >foo : any obj11.bar; >obj11.bar : any ->obj11 : any +>obj11 : string | F >bar : any } diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts index 2750eb96ebfbb..85370505250ee 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts @@ -1,8 +1,36 @@ -interface I { global: string; } -var result: I; -var result2: I; +// @strictNullChecks: true + +interface I { global: string; } +var result!: I; +var result2!: I; if (!(result instanceof RegExp)) { result = result2; } else if (!result.global) { -} \ No newline at end of file +} + +// Repro from #31155 + +interface OnChanges { + onChanges(changes: Record): void +} +interface Validator { + validate(): null | Record; +} + +class C { + validate() { + return {} + } +} + +function foo() { + let v: Validator & Partial = null as any; + if (v instanceof C) { + v // Validator & Partial & C + } + v // Validator & Partial via subtype reduction + if (v.onChanges) { + v.onChanges({}); + } +}