Skip to content

Commit 8c5fa5c

Browse files
author
Orta Therox
authored
Revert assignability cases in getNarrowedType (#42231)
* Revert subtype narrowing changes from readonly array PRs * Adds a test for the change * More baselines
1 parent 1b19af0 commit 8c5fa5c

13 files changed

+453
-35
lines changed

src/compiler/checker.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23094,10 +23094,15 @@ namespace ts {
2309423094
}
2309523095
}
2309623096

23097-
// If the candidate type is a subtype of the target type, narrow to the candidate type,
23098-
// if the target type is a subtype of the candidate type, narrow to the target type,
23099-
// otherwise, narrow to an intersection of the two types.
23100-
return isTypeSubtypeOf(candidate, type) ? candidate : isTypeSubtypeOf(type, candidate) ? type : getIntersectionType([type, candidate]);
23097+
// If the candidate type is a subtype of the target type, narrow to the candidate type.
23098+
// Otherwise, if the target type is assignable to the candidate type, keep the target type.
23099+
// Otherwise, if the candidate type is assignable to the target type, narrow to the candidate
23100+
// type. Otherwise, the types are completely unrelated, so narrow to an intersection of the
23101+
// two types.
23102+
return isTypeSubtypeOf(candidate, type) ? candidate :
23103+
isTypeAssignableTo(type, candidate) ? type :
23104+
isTypeAssignableTo(candidate, type) ? candidate :
23105+
getIntersectionType([type, candidate]);
2310123106
}
2310223107

2310323108
function narrowTypeByCallExpression(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type {

tests/baselines/reference/instanceOfAssignability.types

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ function fn2(x: Base) {
7070
// 1.5: y: Base
7171
// Want: y: Derived1
7272
let y = x;
73-
>y : Base & Derived1
74-
>x : Base & Derived1
73+
>y : Derived1
74+
>x : Derived1
7575
}
7676
}
7777

@@ -104,8 +104,8 @@ function fn4(x: Base|Derived2) {
104104
// 1.5: y: {}
105105
// Want: Derived1
106106
let y = x;
107-
>y : (Base | Derived2) & Derived1
108-
>x : (Base | Derived2) & Derived1
107+
>y : Derived1
108+
>x : Derived1
109109
}
110110
}
111111

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//// [narrowingAssignmentReadonlyRespectsAssertion.ts]
2+
// https://github.com/microsoft/TypeScript/issues/41984
3+
4+
interface TestCase<T extends string | number> {
5+
readonly val1: T | ReadonlyArray<T>;
6+
readonly val2: ReadonlyArray<T>;
7+
}
8+
9+
interface MultiCaseFixture<T> {
10+
cases: T[];
11+
}
12+
13+
function subDataFunc(): TestCase<string | number>[] {
14+
return [
15+
{ val1: "a", val2: ["a", "b", "c"] },
16+
{ val1: 2, val2: [1, 2, 3] },
17+
{ val1: ["a", "z"], val2: ["x", "y", "z"] },
18+
{ val1: [5, 10], val2: [10, 100, 1000] },
19+
];
20+
}
21+
22+
function dataFunc<T>(subFunc: () => T[]): MultiCaseFixture<T> {
23+
return { cases: subFunc() };
24+
}
25+
26+
function testFunc() {
27+
const fixture = dataFunc<TestCase<string | number>>(subDataFunc);
28+
fixture.cases.forEach(({ val1, val2 }) => {
29+
if (Array.isArray(val1)) {
30+
// This should retain val1 as being an array
31+
const reversedVal1 = val1.slice().reverse();
32+
console.log(reversedVal1);
33+
} else {
34+
console.log(val1);
35+
}
36+
console.log(val2);
37+
});
38+
}
39+
40+
testFunc();
41+
42+
43+
//// [narrowingAssignmentReadonlyRespectsAssertion.js]
44+
// https://github.com/microsoft/TypeScript/issues/41984
45+
function subDataFunc() {
46+
return [
47+
{ val1: "a", val2: ["a", "b", "c"] },
48+
{ val1: 2, val2: [1, 2, 3] },
49+
{ val1: ["a", "z"], val2: ["x", "y", "z"] },
50+
{ val1: [5, 10], val2: [10, 100, 1000] },
51+
];
52+
}
53+
function dataFunc(subFunc) {
54+
return { cases: subFunc() };
55+
}
56+
function testFunc() {
57+
var fixture = dataFunc(subDataFunc);
58+
fixture.cases.forEach(function (_a) {
59+
var val1 = _a.val1, val2 = _a.val2;
60+
if (Array.isArray(val1)) {
61+
// This should retain val1 as being an array
62+
var reversedVal1 = val1.slice().reverse();
63+
console.log(reversedVal1);
64+
}
65+
else {
66+
console.log(val1);
67+
}
68+
console.log(val2);
69+
});
70+
}
71+
testFunc();
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
=== tests/cases/compiler/narrowingAssignmentReadonlyRespectsAssertion.ts ===
2+
// https://github.com/microsoft/TypeScript/issues/41984
3+
4+
interface TestCase<T extends string | number> {
5+
>TestCase : Symbol(TestCase, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 0, 0))
6+
>T : Symbol(T, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 2, 19))
7+
8+
readonly val1: T | ReadonlyArray<T>;
9+
>val1 : Symbol(TestCase.val1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 2, 47))
10+
>T : Symbol(T, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 2, 19))
11+
>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --))
12+
>T : Symbol(T, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 2, 19))
13+
14+
readonly val2: ReadonlyArray<T>;
15+
>val2 : Symbol(TestCase.val2, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 3, 38))
16+
>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --))
17+
>T : Symbol(T, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 2, 19))
18+
}
19+
20+
interface MultiCaseFixture<T> {
21+
>MultiCaseFixture : Symbol(MultiCaseFixture, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 5, 1))
22+
>T : Symbol(T, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 7, 27))
23+
24+
cases: T[];
25+
>cases : Symbol(MultiCaseFixture.cases, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 7, 31))
26+
>T : Symbol(T, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 7, 27))
27+
}
28+
29+
function subDataFunc(): TestCase<string | number>[] {
30+
>subDataFunc : Symbol(subDataFunc, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 9, 1))
31+
>TestCase : Symbol(TestCase, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 0, 0))
32+
33+
return [
34+
{ val1: "a", val2: ["a", "b", "c"] },
35+
>val1 : Symbol(val1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 13, 7))
36+
>val2 : Symbol(val2, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 13, 18))
37+
38+
{ val1: 2, val2: [1, 2, 3] },
39+
>val1 : Symbol(val1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 14, 7))
40+
>val2 : Symbol(val2, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 14, 16))
41+
42+
{ val1: ["a", "z"], val2: ["x", "y", "z"] },
43+
>val1 : Symbol(val1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 15, 7))
44+
>val2 : Symbol(val2, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 15, 25))
45+
46+
{ val1: [5, 10], val2: [10, 100, 1000] },
47+
>val1 : Symbol(val1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 16, 7))
48+
>val2 : Symbol(val2, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 16, 22))
49+
50+
];
51+
}
52+
53+
function dataFunc<T>(subFunc: () => T[]): MultiCaseFixture<T> {
54+
>dataFunc : Symbol(dataFunc, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 18, 1))
55+
>T : Symbol(T, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 20, 18))
56+
>subFunc : Symbol(subFunc, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 20, 21))
57+
>T : Symbol(T, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 20, 18))
58+
>MultiCaseFixture : Symbol(MultiCaseFixture, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 5, 1))
59+
>T : Symbol(T, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 20, 18))
60+
61+
return { cases: subFunc() };
62+
>cases : Symbol(cases, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 21, 10))
63+
>subFunc : Symbol(subFunc, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 20, 21))
64+
}
65+
66+
function testFunc() {
67+
>testFunc : Symbol(testFunc, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 22, 1))
68+
69+
const fixture = dataFunc<TestCase<string | number>>(subDataFunc);
70+
>fixture : Symbol(fixture, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 25, 7))
71+
>dataFunc : Symbol(dataFunc, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 18, 1))
72+
>TestCase : Symbol(TestCase, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 0, 0))
73+
>subDataFunc : Symbol(subDataFunc, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 9, 1))
74+
75+
fixture.cases.forEach(({ val1, val2 }) => {
76+
>fixture.cases.forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --))
77+
>fixture.cases : Symbol(MultiCaseFixture.cases, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 7, 31))
78+
>fixture : Symbol(fixture, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 25, 7))
79+
>cases : Symbol(MultiCaseFixture.cases, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 7, 31))
80+
>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --))
81+
>val1 : Symbol(val1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 26, 26))
82+
>val2 : Symbol(val2, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 26, 32))
83+
84+
if (Array.isArray(val1)) {
85+
>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --))
86+
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
87+
>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --))
88+
>val1 : Symbol(val1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 26, 26))
89+
90+
// This should retain val1 as being an array
91+
const reversedVal1 = val1.slice().reverse();
92+
>reversedVal1 : Symbol(reversedVal1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 29, 15))
93+
>val1.slice().reverse : Symbol(Array.reverse, Decl(lib.es5.d.ts, --, --))
94+
>val1.slice : Symbol(Array.slice, Decl(lib.es5.d.ts, --, --))
95+
>val1 : Symbol(val1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 26, 26))
96+
>slice : Symbol(Array.slice, Decl(lib.es5.d.ts, --, --))
97+
>reverse : Symbol(Array.reverse, Decl(lib.es5.d.ts, --, --))
98+
99+
console.log(reversedVal1);
100+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
101+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
102+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
103+
>reversedVal1 : Symbol(reversedVal1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 29, 15))
104+
105+
} else {
106+
console.log(val1);
107+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
108+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
109+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
110+
>val1 : Symbol(val1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 26, 26))
111+
}
112+
console.log(val2);
113+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
114+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
115+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
116+
>val2 : Symbol(val2, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 26, 32))
117+
118+
});
119+
}
120+
121+
testFunc();
122+
>testFunc : Symbol(testFunc, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 22, 1))
123+
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
=== tests/cases/compiler/narrowingAssignmentReadonlyRespectsAssertion.ts ===
2+
// https://github.com/microsoft/TypeScript/issues/41984
3+
4+
interface TestCase<T extends string | number> {
5+
readonly val1: T | ReadonlyArray<T>;
6+
>val1 : T | readonly T[]
7+
8+
readonly val2: ReadonlyArray<T>;
9+
>val2 : readonly T[]
10+
}
11+
12+
interface MultiCaseFixture<T> {
13+
cases: T[];
14+
>cases : T[]
15+
}
16+
17+
function subDataFunc(): TestCase<string | number>[] {
18+
>subDataFunc : () => TestCase<string | number>[]
19+
20+
return [
21+
>[ { val1: "a", val2: ["a", "b", "c"] }, { val1: 2, val2: [1, 2, 3] }, { val1: ["a", "z"], val2: ["x", "y", "z"] }, { val1: [5, 10], val2: [10, 100, 1000] }, ] : ({ val1: string; val2: string[]; } | { val1: number; val2: number[]; } | { val1: string[]; val2: string[]; } | { val1: number[]; val2: number[]; })[]
22+
23+
{ val1: "a", val2: ["a", "b", "c"] },
24+
>{ val1: "a", val2: ["a", "b", "c"] } : { val1: string; val2: string[]; }
25+
>val1 : string
26+
>"a" : "a"
27+
>val2 : string[]
28+
>["a", "b", "c"] : string[]
29+
>"a" : "a"
30+
>"b" : "b"
31+
>"c" : "c"
32+
33+
{ val1: 2, val2: [1, 2, 3] },
34+
>{ val1: 2, val2: [1, 2, 3] } : { val1: number; val2: number[]; }
35+
>val1 : number
36+
>2 : 2
37+
>val2 : number[]
38+
>[1, 2, 3] : number[]
39+
>1 : 1
40+
>2 : 2
41+
>3 : 3
42+
43+
{ val1: ["a", "z"], val2: ["x", "y", "z"] },
44+
>{ val1: ["a", "z"], val2: ["x", "y", "z"] } : { val1: string[]; val2: string[]; }
45+
>val1 : string[]
46+
>["a", "z"] : string[]
47+
>"a" : "a"
48+
>"z" : "z"
49+
>val2 : string[]
50+
>["x", "y", "z"] : string[]
51+
>"x" : "x"
52+
>"y" : "y"
53+
>"z" : "z"
54+
55+
{ val1: [5, 10], val2: [10, 100, 1000] },
56+
>{ val1: [5, 10], val2: [10, 100, 1000] } : { val1: number[]; val2: number[]; }
57+
>val1 : number[]
58+
>[5, 10] : number[]
59+
>5 : 5
60+
>10 : 10
61+
>val2 : number[]
62+
>[10, 100, 1000] : number[]
63+
>10 : 10
64+
>100 : 100
65+
>1000 : 1000
66+
67+
];
68+
}
69+
70+
function dataFunc<T>(subFunc: () => T[]): MultiCaseFixture<T> {
71+
>dataFunc : <T>(subFunc: () => T[]) => MultiCaseFixture<T>
72+
>subFunc : () => T[]
73+
74+
return { cases: subFunc() };
75+
>{ cases: subFunc() } : { cases: T[]; }
76+
>cases : T[]
77+
>subFunc() : T[]
78+
>subFunc : () => T[]
79+
}
80+
81+
function testFunc() {
82+
>testFunc : () => void
83+
84+
const fixture = dataFunc<TestCase<string | number>>(subDataFunc);
85+
>fixture : MultiCaseFixture<TestCase<string | number>>
86+
>dataFunc<TestCase<string | number>>(subDataFunc) : MultiCaseFixture<TestCase<string | number>>
87+
>dataFunc : <T>(subFunc: () => T[]) => MultiCaseFixture<T>
88+
>subDataFunc : () => TestCase<string | number>[]
89+
90+
fixture.cases.forEach(({ val1, val2 }) => {
91+
>fixture.cases.forEach(({ val1, val2 }) => { if (Array.isArray(val1)) { // This should retain val1 as being an array const reversedVal1 = val1.slice().reverse(); console.log(reversedVal1); } else { console.log(val1); } console.log(val2); }) : void
92+
>fixture.cases.forEach : (callbackfn: (value: TestCase<string | number>, index: number, array: TestCase<string | number>[]) => void, thisArg?: any) => void
93+
>fixture.cases : TestCase<string | number>[]
94+
>fixture : MultiCaseFixture<TestCase<string | number>>
95+
>cases : TestCase<string | number>[]
96+
>forEach : (callbackfn: (value: TestCase<string | number>, index: number, array: TestCase<string | number>[]) => void, thisArg?: any) => void
97+
>({ val1, val2 }) => { if (Array.isArray(val1)) { // This should retain val1 as being an array const reversedVal1 = val1.slice().reverse(); console.log(reversedVal1); } else { console.log(val1); } console.log(val2); } : ({ val1, val2 }: TestCase<string | number>) => void
98+
>val1 : string | number | readonly (string | number)[]
99+
>val2 : readonly (string | number)[]
100+
101+
if (Array.isArray(val1)) {
102+
>Array.isArray(val1) : boolean
103+
>Array.isArray : (arg: any) => arg is any[]
104+
>Array : ArrayConstructor
105+
>isArray : (arg: any) => arg is any[]
106+
>val1 : string | number | readonly (string | number)[]
107+
108+
// This should retain val1 as being an array
109+
const reversedVal1 = val1.slice().reverse();
110+
>reversedVal1 : any[]
111+
>val1.slice().reverse() : any[]
112+
>val1.slice().reverse : () => any[]
113+
>val1.slice() : any[]
114+
>val1.slice : (start?: number, end?: number) => any[]
115+
>val1 : any[]
116+
>slice : (start?: number, end?: number) => any[]
117+
>reverse : () => any[]
118+
119+
console.log(reversedVal1);
120+
>console.log(reversedVal1) : void
121+
>console.log : (...data: any[]) => void
122+
>console : Console
123+
>log : (...data: any[]) => void
124+
>reversedVal1 : any[]
125+
126+
} else {
127+
console.log(val1);
128+
>console.log(val1) : void
129+
>console.log : (...data: any[]) => void
130+
>console : Console
131+
>log : (...data: any[]) => void
132+
>val1 : string | number | readonly (string | number)[]
133+
}
134+
console.log(val2);
135+
>console.log(val2) : void
136+
>console.log : (...data: any[]) => void
137+
>console : Console
138+
>log : (...data: any[]) => void
139+
>val2 : readonly (string | number)[]
140+
141+
});
142+
}
143+
144+
testFunc();
145+
>testFunc() : void
146+
>testFunc : () => void
147+

0 commit comments

Comments
 (0)