Skip to content

Commit

Permalink
A second attempt at readonly array support persisting through isArray
Browse files Browse the repository at this point in the history
  • Loading branch information
orta committed Jan 13, 2021
1 parent a894f8a commit 6cd8141
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 37 deletions.
2 changes: 1 addition & 1 deletion lib/lib.es5.d.ts
Expand Up @@ -1408,7 +1408,7 @@ interface ArrayConstructor {
(arrayLength?: number): any[];
<T>(arrayLength: number): T[];
<T>(...items: T[]): T[];
isArray(arg: any): arg is any[];
isArray(arg: any): arg is readonly unknown[];
readonly prototype: any[];
}

Expand Down
15 changes: 10 additions & 5 deletions src/compiler/checker.ts
Expand Up @@ -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)) {
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/instanceOfAssignability.types
Expand Up @@ -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
}
}

Expand Down Expand Up @@ -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
}
}

Expand Down
16 changes: 8 additions & 8 deletions tests/baselines/reference/typeGuardIntersectionTypes.symbols
Expand Up @@ -176,37 +176,37 @@ 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))
}
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))
}
}

// All non-winged beasts with legs
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))
}
}

Expand Down
10 changes: 5 additions & 5 deletions tests/baselines/reference/typeGuardIntersectionTypes.types
Expand Up @@ -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

Expand All @@ -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

Expand All @@ -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
}
}
Expand All @@ -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
}
}
Expand Down
12 changes: 11 additions & 1 deletion 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<OnChanges>)'.
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<OnChanges>)'.
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;
Expand Down Expand Up @@ -37,7 +41,13 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts(7,20)
}
v // Validator & Partial<OnChanges> via subtype reduction
if (v.onChanges) {
~~~~~~~~~
!!! error TS2339: Property 'onChanges' does not exist on type 'C | (Validator & Partial<OnChanges>)'.
!!! error TS2339: Property 'onChanges' does not exist on type 'C'.
v.onChanges({});
~~~~~~~~~
!!! error TS2339: Property 'onChanges' does not exist on type 'C | (Validator & Partial<OnChanges>)'.
!!! error TS2339: Property 'onChanges' does not exist on type 'C'.
}
}

Expand Down
4 changes: 0 additions & 4 deletions tests/baselines/reference/typeGuardsWithInstanceOf.symbols
Expand Up @@ -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))
}
}

Expand Down
18 changes: 9 additions & 9 deletions tests/baselines/reference/typeGuardsWithInstanceOf.types
Expand Up @@ -65,21 +65,21 @@ function foo() {
>C : typeof C

v // Validator & Partial<OnChanges> & C
>v : Validator & Partial<OnChanges> & C
>v : C
}
v // Validator & Partial<OnChanges> via subtype reduction
>v : Validator & Partial<OnChanges>
>v : C | (Validator & Partial<OnChanges>)

if (v.onChanges) {
>v.onChanges : ((changes: Record<string, unknown>) => void) | undefined
>v : Validator & Partial<OnChanges>
>onChanges : ((changes: Record<string, unknown>) => void) | undefined
>v.onChanges : any
>v : C | (Validator & Partial<OnChanges>)
>onChanges : any

v.onChanges({});
>v.onChanges({}) : void
>v.onChanges : (changes: Record<string, unknown>) => void
>v : Validator & Partial<OnChanges>
>onChanges : (changes: Record<string, unknown>) => void
>v.onChanges({}) : any
>v.onChanges : any
>v : C | (Validator & Partial<OnChanges>)
>onChanges : any
>{} : {}
}
}
Expand Down

0 comments on commit 6cd8141

Please sign in to comment.