Skip to content

Commit 103f894

Browse files
authored
Merge pull request #26425 from mattmccutchen/issue-26405
Go back to old narrowing algorithm but don't narrow in cases like #26130
2 parents 435a12e + cc1c2ab commit 103f894

File tree

7 files changed

+78
-6
lines changed

7 files changed

+78
-6
lines changed

src/compiler/checker.ts

+18-2
Original file line numberDiff line numberDiff line change
@@ -13900,6 +13900,18 @@ namespace ts {
1390013900
return flow.id;
1390113901
}
1390213902

13903+
function typeMaybeAssignableTo(source: Type, target: Type) {
13904+
if (!(source.flags & TypeFlags.Union)) {
13905+
return isTypeAssignableTo(source, target);
13906+
}
13907+
for (const t of (<UnionType>source).types) {
13908+
if (isTypeAssignableTo(t, target)) {
13909+
return true;
13910+
}
13911+
}
13912+
return false;
13913+
}
13914+
1390313915
// Remove those constituent types of declaredType to which no constituent type of assignedType is assignable.
1390413916
// For example, when a variable of type number | string | boolean is assigned a value of type number | boolean,
1390513917
// we remove type string.
@@ -13908,8 +13920,12 @@ namespace ts {
1390813920
if (assignedType.flags & TypeFlags.Never) {
1390913921
return assignedType;
1391013922
}
13911-
const reducedType = filterType(declaredType, t => isTypeComparableTo(assignedType, t));
13912-
if (!(reducedType.flags & TypeFlags.Never)) {
13923+
const reducedType = filterType(declaredType, t => typeMaybeAssignableTo(assignedType, t));
13924+
// Our crude heuristic produces an invalid result in some cases: see GH#26130.
13925+
// For now, when that happens, we give up and don't narrow at all. (This also
13926+
// means we'll never narrow for erroneous assignments where the assigned type
13927+
// is not assignable to the declared type.)
13928+
if (isTypeAssignableTo(assignedType, reducedType)) {
1391313929
return reducedType;
1391413930
}
1391513931
}

tests/baselines/reference/assignmentTypeNarrowing.js

+8
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ let a: string[];
2727
for (x of a) {
2828
x; // string
2929
}
30+
31+
// Repro from #26405
32+
33+
type AOrArrA<T> = T | T[];
34+
const arr: AOrArrA<{x?: "ok"}> = [{ x: "ok" }]; // weak type
35+
arr.push({ x: "ok" });
3036

3137

3238
//// [assignmentTypeNarrowing.js]
@@ -51,3 +57,5 @@ for (var _i = 0, a_1 = a; _i < a_1.length; _i++) {
5157
x = a_1[_i];
5258
x; // string
5359
}
60+
var arr = [{ x: "ok" }]; // weak type
61+
arr.push({ x: "ok" });

tests/baselines/reference/assignmentTypeNarrowing.symbols

+20
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,23 @@ for (x of a) {
6262
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3))
6363
}
6464

65+
// Repro from #26405
66+
67+
type AOrArrA<T> = T | T[];
68+
>AOrArrA : Symbol(AOrArrA, Decl(assignmentTypeNarrowing.ts, 27, 1))
69+
>T : Symbol(T, Decl(assignmentTypeNarrowing.ts, 31, 13))
70+
>T : Symbol(T, Decl(assignmentTypeNarrowing.ts, 31, 13))
71+
>T : Symbol(T, Decl(assignmentTypeNarrowing.ts, 31, 13))
72+
73+
const arr: AOrArrA<{x?: "ok"}> = [{ x: "ok" }]; // weak type
74+
>arr : Symbol(arr, Decl(assignmentTypeNarrowing.ts, 32, 5))
75+
>AOrArrA : Symbol(AOrArrA, Decl(assignmentTypeNarrowing.ts, 27, 1))
76+
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 32, 20))
77+
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 32, 35))
78+
79+
arr.push({ x: "ok" });
80+
>arr.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
81+
>arr : Symbol(arr, Decl(assignmentTypeNarrowing.ts, 32, 5))
82+
>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
83+
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 33, 10))
84+

tests/baselines/reference/assignmentTypeNarrowing.types

+22
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,25 @@ for (x of a) {
9696
>x : string
9797
}
9898

99+
// Repro from #26405
100+
101+
type AOrArrA<T> = T | T[];
102+
>AOrArrA : AOrArrA<T>
103+
104+
const arr: AOrArrA<{x?: "ok"}> = [{ x: "ok" }]; // weak type
105+
>arr : AOrArrA<{ x?: "ok"; }>
106+
>x : "ok"
107+
>[{ x: "ok" }] : { x: "ok"; }[]
108+
>{ x: "ok" } : { x: "ok"; }
109+
>x : "ok"
110+
>"ok" : "ok"
111+
112+
arr.push({ x: "ok" });
113+
>arr.push({ x: "ok" }) : number
114+
>arr.push : (...items: { x?: "ok"; }[]) => number
115+
>arr : { x?: "ok"; }[]
116+
>push : (...items: { x?: "ok"; }[]) => number
117+
>{ x: "ok" } : { x: "ok"; }
118+
>x : "ok"
119+
>"ok" : "ok"
120+

tests/baselines/reference/enumAssignmentCompat3.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,9 @@ abc = merged; // missing 'd'
252252
>merged : Merged.E
253253

254254
merged = abc; // ok
255-
>merged = abc : First.E.a | First.E.b
255+
>merged = abc : First.E
256256
>merged : Merged.E
257-
>abc : First.E.a | First.E.b
257+
>abc : First.E
258258

259259
abc = merged2; // ok
260260
>abc = merged2 : Merged2.E

tests/baselines/reference/numericLiteralTypes3.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,9 @@ function f4(a: A, b: B, c: C, d: D) {
118118
>c : C
119119

120120
d = d;
121-
>d = d : 1 | 2
121+
>d = d : D
122+
>d : D
122123
>d : D
123-
>d : 1 | 2
124124
}
125125

126126
function f5(a: A, b: B, c: C, d: D) {

tests/cases/conformance/expressions/assignmentOperator/assignmentTypeNarrowing.ts

+6
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,9 @@ let a: string[];
2626
for (x of a) {
2727
x; // string
2828
}
29+
30+
// Repro from #26405
31+
32+
type AOrArrA<T> = T | T[];
33+
const arr: AOrArrA<{x?: "ok"}> = [{ x: "ok" }]; // weak type
34+
arr.push({ x: "ok" });

0 commit comments

Comments
 (0)