From f6b438bc4a8c143c1a70287df4727351ae9d257c Mon Sep 17 00:00:00 2001 From: Orta Date: Tue, 12 Jan 2021 19:24:10 +0000 Subject: [PATCH] A second attempt at readonly array support persisting through isArray --- lib/lib.es5.d.ts | 2 +- src/compiler/checker.ts | 15 ++++++++++----- .../reference/instanceOfAssignability.types | 8 ++++---- .../typeGuardIntersectionTypes.symbols | 16 ++++++++-------- .../reference/typeGuardIntersectionTypes.types | 10 +++++----- .../typeGuardsWithInstanceOf.errors.txt | 12 +++++++++++- .../reference/typeGuardsWithInstanceOf.symbols | 4 ---- .../reference/typeGuardsWithInstanceOf.types | 18 +++++++++--------- 8 files changed, 48 insertions(+), 37 deletions(-) diff --git a/lib/lib.es5.d.ts b/lib/lib.es5.d.ts index 221882e80f6d5..ab6a5d0fb3806 100644 --- a/lib/lib.es5.d.ts +++ b/lib/lib.es5.d.ts @@ -1408,7 +1408,7 @@ interface ArrayConstructor { (arrayLength?: number): any[]; (arrayLength: number): T[]; (...items: T[]): T[]; - isArray(arg: any): arg is any[]; + isArray(arg: any): arg is readonly unknown[]; readonly prototype: any[]; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 10f8dc923b88f..fab3f26ea4d40 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22999,11 +22999,16 @@ namespace ts { } } - // 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]); - } + // 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]); + } function narrowTypeByCallExpression(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type { if (hasMatchingArgument(callExpression, reference)) { diff --git a/tests/baselines/reference/instanceOfAssignability.types b/tests/baselines/reference/instanceOfAssignability.types index 9c29abbe89491..4e65e644a3b54 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 : Base & Derived1 ->x : Base & Derived1 +>y : Derived1 +>x : Derived1 } } @@ -104,8 +104,8 @@ function fn4(x: Base|Derived2) { // 1.5: y: {} // Want: Derived1 let y = x; ->y : (Base | Derived2) & Derived1 ->x : (Base | Derived2) & Derived1 +>y : Derived1 +>x : Derived1 } } diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.symbols b/tests/baselines/reference/typeGuardIntersectionTypes.symbols index f99148a9f0500..21da29e434fec 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(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 64, 23)) ->legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>legs : Symbol(Legged.legs, 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(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 64, 23)) ->legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>legs : Symbol(Legged.legs, 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(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 64, 23)) ->legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>legs : Symbol(Legged.legs, 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(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 64, 23)) ->legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) } } diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.types b/tests/baselines/reference/typeGuardIntersectionTypes.types index 6d9d0f47ac470..1d871fe3e2e19 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 : Beast & Legged +>beast : Legged if (beast.legs === 4) { >beast.legs === 4 : boolean >beast.legs : number ->beast : Beast & Legged & Winged +>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 : Beast & Legged & Winged +>beast : Legged & Winged >legs : number >2 : 2 @@ -198,7 +198,7 @@ function identifyBeast(beast: Beast) { >log : (s: string) => void >`unknown - ${beast.legs} legs, wings` : `unknown - ${number} legs, wings` >beast.legs : number ->beast : Beast & Legged & Winged +>beast : Legged & Winged >legs : number } } @@ -210,7 +210,7 @@ function identifyBeast(beast: Beast) { >log : (s: string) => void >`manbearpig - ${beast.legs} legs, no wings` : `manbearpig - ${number} legs, no wings` >beast.legs : number ->beast : Beast & Legged +>beast : Legged >legs : number } } diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.errors.txt b/tests/baselines/reference/typeGuardsWithInstanceOf.errors.txt index c6f44d4f15274..013fa4652f02d 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOf.errors.txt +++ b/tests/baselines/reference/typeGuardsWithInstanceOf.errors.txt @@ -1,8 +1,12 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts(7,20): error TS2339: Property 'global' does not exist on type 'never'. The intersection 'I & RegExp' was reduced to 'never' because property 'global' has conflicting types in some constituents. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts(31,11): error TS2339: Property 'onChanges' does not exist on type 'C | (Validator & Partial)'. + Property 'onChanges' does not exist on type 'C'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts(32,11): error TS2339: Property 'onChanges' does not exist on type 'C | (Validator & Partial)'. + Property 'onChanges' does not exist on type 'C'. -==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts (1 errors) ==== +==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts (3 errors) ==== interface I { global: string; } var result!: I; var result2!: I; @@ -37,7 +41,13 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts(7,20) } v // Validator & Partial via subtype reduction if (v.onChanges) { + ~~~~~~~~~ +!!! error TS2339: Property 'onChanges' does not exist on type 'C | (Validator & Partial)'. +!!! error TS2339: Property 'onChanges' does not exist on type 'C'. v.onChanges({}); + ~~~~~~~~~ +!!! error TS2339: Property 'onChanges' does not exist on type 'C | (Validator & Partial)'. +!!! error TS2339: Property 'onChanges' does not exist on type 'C'. } } diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.symbols b/tests/baselines/reference/typeGuardsWithInstanceOf.symbols index 018c6bac6da52..ad97696c31840 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOf.symbols +++ b/tests/baselines/reference/typeGuardsWithInstanceOf.symbols @@ -71,14 +71,10 @@ function foo() { >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 198769effc2c4..b2022d70e8cdd 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOf.types +++ b/tests/baselines/reference/typeGuardsWithInstanceOf.types @@ -65,21 +65,21 @@ function foo() { >C : typeof C v // Validator & Partial & C ->v : Validator & Partial & C +>v : C } v // Validator & Partial via subtype reduction ->v : Validator & Partial +>v : C | (Validator & Partial) if (v.onChanges) { ->v.onChanges : ((changes: Record) => void) | undefined ->v : Validator & Partial ->onChanges : ((changes: Record) => void) | undefined +>v.onChanges : any +>v : C | (Validator & Partial) +>onChanges : any v.onChanges({}); ->v.onChanges({}) : void ->v.onChanges : (changes: Record) => void ->v : Validator & Partial ->onChanges : (changes: Record) => void +>v.onChanges({}) : any +>v.onChanges : any +>v : C | (Validator & Partial) +>onChanges : any >{} : {} } }