Skip to content

Commit cc1c2ab

Browse files
committed
Go back to the old narrowing algorithm (pre #26143) and avoid #26130 by
skipping narrowing if the old algorithm produces a type to which the assigned type is not assignable. This also means we'll no longer narrow for erroneous assignments where the assigned type is not assignable to the declared type. This is the reason for the numericLiteralTypes3 baseline change. Fixes #26405.
1 parent 926bdee commit cc1c2ab

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)