Skip to content

Commit b6026c2

Browse files
committed
Add subtype case
1 parent 2b8ce2d commit b6026c2

12 files changed

+232
-30
lines changed

src/compiler/checker.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17652,9 +17652,10 @@ namespace ts {
1765217652
return assignableType;
1765317653
}
1765417654
}
17655-
// If the candidate type is a subtype of the target type, narrow to the candidate type.
17656-
// Otherwise, narrow to an intersection of the two types.
17657-
return isTypeSubtypeOf(candidate, type) ? candidate : getIntersectionType([type, candidate]);
17655+
// If the candidate type is a subtype of the target type, narrow to the candidate type,
17656+
// if the target type is a subtype of the candidate type, narrow to the target type,
17657+
// otherwise, narrow to an intersection of the two types.
17658+
return isTypeSubtypeOf(candidate, type) ? candidate : isTypeSubtypeOf(type, candidate) ? type : getIntersectionType([type, candidate]);
1765817659
}
1765917660

1766017661
function narrowTypeByTypePredicate(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type {

tests/baselines/reference/controlFlowInstanceof.symbols

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ function f1(s: Set<string> | Set<number>) {
2525
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 2, 12))
2626

2727
s.add(42);
28-
>s.add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --))
28+
>s.add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --))
2929
>s : Symbol(s, Decl(controlFlowInstanceof.ts, 2, 12))
30-
>add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --))
30+
>add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --))
3131
}
3232

3333
function f2(s: Set<string> | Set<number>) {

tests/baselines/reference/controlFlowInstanceof.types

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@ function f1(s: Set<string> | Set<number>) {
2020
>Set : SetConstructor
2121

2222
s; // Set<number>
23-
>s : Set<number> & Set<any>
23+
>s : Set<number>
2424
}
2525
s; // Set<number>
26-
>s : Set<number> & Set<any>
26+
>s : Set<number>
2727

2828
s.add(42);
29-
>s.add(42) : Set<number> & Set<any>
30-
>s.add : ((value: number) => Set<number> & Set<any>) & ((value: any) => Set<number> & Set<any>)
31-
>s : Set<number> & Set<any>
32-
>add : ((value: number) => Set<number> & Set<any>) & ((value: any) => Set<number> & Set<any>)
29+
>s.add(42) : Set<number>
30+
>s.add : (value: number) => Set<number>
31+
>s : Set<number>
32+
>add : (value: number) => Set<number>
3333
>42 : 42
3434
}
3535

@@ -105,7 +105,7 @@ function f4(s: Set<string> | Set<number>) {
105105
>Set : SetConstructor
106106

107107
s; // Set<number>
108-
>s : Set<number> & Set<any>
108+
>s : Set<number>
109109
}
110110
else {
111111
s; // never

tests/baselines/reference/instanceofWithPrimitiveUnion.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ function test1(x: number | string) {
99
>Object : ObjectConstructor
1010

1111
x;
12-
>x : (string & Object) | (number & Object)
12+
>x : string | number
1313
}
1414
}
1515

@@ -23,7 +23,7 @@ function test2(x: (number | string) | number) {
2323
>Object : ObjectConstructor
2424

2525
x;
26-
>x : (string & Object) | (number & Object)
26+
>x : string | number
2727
}
2828
}
2929

tests/baselines/reference/narrowingConstrainedTypeParameter.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,5 @@ export function speak<TPet extends Pet>(pet: TPet, voice: (pet: TPet) => string)
3939
return voice(pet);
4040
>voice(pet) : string
4141
>voice : (pet: TPet) => string
42-
>pet : TPet & Pet
42+
>pet : TPet
4343
}

tests/baselines/reference/typeGuardFunction.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ if(isA(subType)) {
6565

6666
subType.propC;
6767
>subType.propC : number
68-
>subType : C & A
68+
>subType : C
6969
>propC : number
7070
}
7171

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,39 @@
11
//// [typeGuardsWithInstanceOf.ts]
22
interface I { global: string; }
3-
var result: I;
4-
var result2: I;
3+
var result!: I;
4+
var result2!: I;
55

66
if (!(result instanceof RegExp)) {
77
result = result2;
88
} else if (!result.global) {
9-
}
9+
}
10+
11+
// Repro from #31155
12+
13+
interface OnChanges {
14+
onChanges(changes: Record<string, unknown>): void
15+
}
16+
interface Validator {
17+
validate(): null | Record<string, unknown>;
18+
}
19+
20+
class C {
21+
validate() {
22+
return {}
23+
}
24+
}
25+
26+
function foo() {
27+
let v: Validator & Partial<OnChanges> = null as any;
28+
if (v instanceof C) {
29+
v // Validator & Partial<OnChanges> & C
30+
}
31+
v // Validator & Partial<OnChanges> via subtype reduction
32+
if (v.onChanges) {
33+
v.onChanges({});
34+
}
35+
}
36+
1037

1138
//// [typeGuardsWithInstanceOf.js]
1239
var result;
@@ -16,3 +43,21 @@ if (!(result instanceof RegExp)) {
1643
}
1744
else if (!result.global) {
1845
}
46+
var C = /** @class */ (function () {
47+
function C() {
48+
}
49+
C.prototype.validate = function () {
50+
return {};
51+
};
52+
return C;
53+
}());
54+
function foo() {
55+
var v = null;
56+
if (v instanceof C) {
57+
v; // Validator & Partial<OnChanges> & C
58+
}
59+
v; // Validator & Partial<OnChanges> via subtype reduction
60+
if (v.onChanges) {
61+
v.onChanges({});
62+
}
63+
}

tests/baselines/reference/typeGuardsWithInstanceOf.symbols

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ interface I { global: string; }
33
>I : Symbol(I, Decl(typeGuardsWithInstanceOf.ts, 0, 0))
44
>global : Symbol(I.global, Decl(typeGuardsWithInstanceOf.ts, 0, 13))
55

6-
var result: I;
6+
var result!: I;
77
>result : Symbol(result, Decl(typeGuardsWithInstanceOf.ts, 1, 3))
88
>I : Symbol(I, Decl(typeGuardsWithInstanceOf.ts, 0, 0))
99

10-
var result2: I;
10+
var result2!: I;
1111
>result2 : Symbol(result2, Decl(typeGuardsWithInstanceOf.ts, 2, 3))
1212
>I : Symbol(I, Decl(typeGuardsWithInstanceOf.ts, 0, 0))
1313

@@ -24,3 +24,63 @@ if (!(result instanceof RegExp)) {
2424
>result : Symbol(result, Decl(typeGuardsWithInstanceOf.ts, 1, 3))
2525
>global : Symbol(global, Decl(typeGuardsWithInstanceOf.ts, 0, 13), Decl(lib.es5.d.ts, --, --))
2626
}
27+
28+
// Repro from #31155
29+
30+
interface OnChanges {
31+
>OnChanges : Symbol(OnChanges, Decl(typeGuardsWithInstanceOf.ts, 7, 1))
32+
33+
onChanges(changes: Record<string, unknown>): void
34+
>onChanges : Symbol(OnChanges.onChanges, Decl(typeGuardsWithInstanceOf.ts, 11, 21))
35+
>changes : Symbol(changes, Decl(typeGuardsWithInstanceOf.ts, 12, 14))
36+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
37+
}
38+
interface Validator {
39+
>Validator : Symbol(Validator, Decl(typeGuardsWithInstanceOf.ts, 13, 1))
40+
41+
validate(): null | Record<string, unknown>;
42+
>validate : Symbol(Validator.validate, Decl(typeGuardsWithInstanceOf.ts, 14, 21))
43+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
44+
}
45+
46+
class C {
47+
>C : Symbol(C, Decl(typeGuardsWithInstanceOf.ts, 16, 1))
48+
49+
validate() {
50+
>validate : Symbol(C.validate, Decl(typeGuardsWithInstanceOf.ts, 18, 9))
51+
52+
return {}
53+
}
54+
}
55+
56+
function foo() {
57+
>foo : Symbol(foo, Decl(typeGuardsWithInstanceOf.ts, 22, 1))
58+
59+
let v: Validator & Partial<OnChanges> = null as any;
60+
>v : Symbol(v, Decl(typeGuardsWithInstanceOf.ts, 25, 7))
61+
>Validator : Symbol(Validator, Decl(typeGuardsWithInstanceOf.ts, 13, 1))
62+
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
63+
>OnChanges : Symbol(OnChanges, Decl(typeGuardsWithInstanceOf.ts, 7, 1))
64+
65+
if (v instanceof C) {
66+
>v : Symbol(v, Decl(typeGuardsWithInstanceOf.ts, 25, 7))
67+
>C : Symbol(C, Decl(typeGuardsWithInstanceOf.ts, 16, 1))
68+
69+
v // Validator & Partial<OnChanges> & C
70+
>v : Symbol(v, Decl(typeGuardsWithInstanceOf.ts, 25, 7))
71+
}
72+
v // Validator & Partial<OnChanges> via subtype reduction
73+
>v : Symbol(v, Decl(typeGuardsWithInstanceOf.ts, 25, 7))
74+
75+
if (v.onChanges) {
76+
>v.onChanges : Symbol(onChanges, Decl(typeGuardsWithInstanceOf.ts, 11, 21))
77+
>v : Symbol(v, Decl(typeGuardsWithInstanceOf.ts, 25, 7))
78+
>onChanges : Symbol(onChanges, Decl(typeGuardsWithInstanceOf.ts, 11, 21))
79+
80+
v.onChanges({});
81+
>v.onChanges : Symbol(onChanges, Decl(typeGuardsWithInstanceOf.ts, 11, 21))
82+
>v : Symbol(v, Decl(typeGuardsWithInstanceOf.ts, 25, 7))
83+
>onChanges : Symbol(onChanges, Decl(typeGuardsWithInstanceOf.ts, 11, 21))
84+
}
85+
}
86+

tests/baselines/reference/typeGuardsWithInstanceOf.types

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
interface I { global: string; }
33
>global : string
44

5-
var result: I;
5+
var result!: I;
66
>result : I
77

8-
var result2: I;
8+
var result2!: I;
99
>result2 : I
1010

1111
if (!(result instanceof RegExp)) {
@@ -26,3 +26,61 @@ if (!(result instanceof RegExp)) {
2626
>result : I & RegExp
2727
>global : never
2828
}
29+
30+
// Repro from #31155
31+
32+
interface OnChanges {
33+
onChanges(changes: Record<string, unknown>): void
34+
>onChanges : (changes: Record<string, unknown>) => void
35+
>changes : Record<string, unknown>
36+
}
37+
interface Validator {
38+
validate(): null | Record<string, unknown>;
39+
>validate : () => Record<string, unknown> | null
40+
>null : null
41+
}
42+
43+
class C {
44+
>C : C
45+
46+
validate() {
47+
>validate : () => {}
48+
49+
return {}
50+
>{} : {}
51+
}
52+
}
53+
54+
function foo() {
55+
>foo : () => void
56+
57+
let v: Validator & Partial<OnChanges> = null as any;
58+
>v : Validator & Partial<OnChanges>
59+
>null as any : any
60+
>null : null
61+
62+
if (v instanceof C) {
63+
>v instanceof C : boolean
64+
>v : Validator & Partial<OnChanges>
65+
>C : typeof C
66+
67+
v // Validator & Partial<OnChanges> & C
68+
>v : Validator & Partial<OnChanges> & C
69+
}
70+
v // Validator & Partial<OnChanges> via subtype reduction
71+
>v : Validator & Partial<OnChanges>
72+
73+
if (v.onChanges) {
74+
>v.onChanges : ((changes: Record<string, unknown>) => void) | undefined
75+
>v : Validator & Partial<OnChanges>
76+
>onChanges : ((changes: Record<string, unknown>) => void) | undefined
77+
78+
v.onChanges({});
79+
>v.onChanges({}) : void
80+
>v.onChanges : (changes: Record<string, unknown>) => void
81+
>v : Validator & Partial<OnChanges>
82+
>onChanges : (changes: Record<string, unknown>) => void
83+
>{} : {}
84+
}
85+
}
86+

tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,18 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru
1515
Property 'bar1' does not exist on type 'E2'.
1616
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(119,11): error TS2339: Property 'bar2' does not exist on type 'E1 | E2'.
1717
Property 'bar2' does not exist on type 'E1'.
18+
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(134,11): error TS2339: Property 'foo' does not exist on type 'string | F'.
19+
Property 'foo' does not exist on type 'string'.
20+
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(135,11): error TS2339: Property 'bar' does not exist on type 'string | F'.
21+
Property 'bar' does not exist on type 'string'.
1822
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(160,11): error TS2339: Property 'foo2' does not exist on type 'G1'.
1923
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(166,11): error TS2339: Property 'foo2' does not exist on type 'G1'.
2024
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(182,11): error TS2339: Property 'bar' does not exist on type 'H'.
2125
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(187,11): error TS2551: Property 'foo1' does not exist on type 'H'. Did you mean 'foo'?
2226
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(188,11): error TS2551: Property 'foo2' does not exist on type 'H'. Did you mean 'foo'?
2327

2428

25-
==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts (18 errors) ====
29+
==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts (20 errors) ====
2630
interface AConstructor {
2731
new (): A;
2832
}
@@ -187,7 +191,13 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru
187191
var obj11: F | string;
188192
if (obj11 instanceof F) { // can't type narrowing, construct signature returns any.
189193
obj11.foo;
194+
~~~
195+
!!! error TS2339: Property 'foo' does not exist on type 'string | F'.
196+
!!! error TS2339: Property 'foo' does not exist on type 'string'.
190197
obj11.bar;
198+
~~~
199+
!!! error TS2339: Property 'bar' does not exist on type 'string | F'.
200+
!!! error TS2339: Property 'bar' does not exist on type 'string'.
191201
}
192202

193203
var obj12: any;

tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,12 +341,12 @@ if (obj11 instanceof F) { // can't type narrowing, construct signature returns a
341341

342342
obj11.foo;
343343
>obj11.foo : any
344-
>obj11 : any
344+
>obj11 : string | F
345345
>foo : any
346346

347347
obj11.bar;
348348
>obj11.bar : any
349-
>obj11 : any
349+
>obj11 : string | F
350350
>bar : any
351351
}
352352

0 commit comments

Comments
 (0)