Skip to content

Commit 028f14c

Browse files
authored
Filter origin types when filtering union types (#42378)
* When filtering a union type, also filter its origin type, if any * Accept new baselines
1 parent 1cef53f commit 028f14c

7 files changed

+30
-10
lines changed

src/compiler/checker.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21689,7 +21689,27 @@ namespace ts {
2168921689
if (type.flags & TypeFlags.Union) {
2169021690
const types = (<UnionType>type).types;
2169121691
const filtered = filter(types, f);
21692-
return filtered === types ? type : getUnionTypeFromSortedList(filtered, (<UnionType>type).objectFlags);
21692+
if (filtered === types) {
21693+
return type;
21694+
}
21695+
const origin = (<UnionType>type).origin;
21696+
let newOrigin: Type | undefined;
21697+
if (origin && origin.flags & TypeFlags.Union) {
21698+
// If the origin type is a (denormalized) union type, filter its non-union constituents. If that ends
21699+
// up removing a smaller number of types than in the normalized constituent set (meaning some of the
21700+
// filtered types are within nested unions in the origin), then we can't construct a new origin type.
21701+
// Otherwise, if we have exactly one type left in the origin set, return that as the filtered type.
21702+
// Otherwise, construct a new filtered origin type.
21703+
const originTypes = (<UnionType>origin).types;
21704+
const originFiltered = filter(originTypes, t => !!(t.flags & TypeFlags.Union) || f(t));
21705+
if (originTypes.length - originFiltered.length === types.length - filtered.length) {
21706+
if (originFiltered.length === 1) {
21707+
return originFiltered[0];
21708+
}
21709+
newOrigin = createOriginUnionOrIntersectionType(TypeFlags.Union, originFiltered);
21710+
}
21711+
}
21712+
return getUnionTypeFromSortedList(filtered, (<UnionType>type).objectFlags, /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, newOrigin);
2169321713
}
2169421714
return type.flags & TypeFlags.Never || f(type) ? type : neverType;
2169521715
}

tests/baselines/reference/exhaustiveSwitchStatements1.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts(7,9): error TS7027: Unreachable code detected.
2-
tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts(235,5): error TS2367: This condition will always return 'false' since the types '"a" | "b"' and '"c"' have no overlap.
2+
tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts(235,5): error TS2367: This condition will always return 'false' since the types 'keyof O' and '"c"' have no overlap.
33

44

55
==== tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts (2 errors) ====
@@ -241,7 +241,7 @@ tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts(235,5): error
241241
}
242242
k === 'c'; // Error
243243
~~~~~~~~~
244-
!!! error TS2367: This condition will always return 'false' since the types '"a" | "b"' and '"c"' have no overlap.
244+
!!! error TS2367: This condition will always return 'false' since the types 'keyof O' and '"c"' have no overlap.
245245
return o[k];
246246
}
247247

tests/baselines/reference/exhaustiveSwitchStatements1.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -695,12 +695,12 @@ function ff(o: O, k: K) {
695695
}
696696
k === 'c'; // Error
697697
>k === 'c' : boolean
698-
>k : "a" | "b"
698+
>k : keyof O
699699
>'c' : "c"
700700

701701
return o[k];
702702
>o[k] : number
703703
>o : O
704-
>k : "a" | "b"
704+
>k : keyof O
705705
}
706706

tests/baselines/reference/observableInferenceCanBeMade.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,6 @@ function asObservable(input: string | ObservableInput<string>): Observable<strin
5252
>input : string
5353
>from(input) : Observable<string>
5454
>from : <O extends ObservableInput<any>>(input: O) => Observable<ObservedValueOf<O>>
55-
>input : Subscribable<never> | Subscribable<string>
55+
>input : ObservableInput<string>
5656
}
5757

tests/baselines/reference/partiallyDiscriminantedUnions.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ function fail(s: Shapes) {
9595
if (s.kind === "circle") {
9696
>s.kind === "circle" : boolean
9797
>s.kind : "square" | "circle"
98-
>s : Square | Circle
98+
>s : Shape
9999
>kind : "square" | "circle"
100100
>"circle" : "circle"
101101

tests/baselines/reference/spreadBooleanRespectsFreshness.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@ foo1 = [...Array.isArray(foo2) ? foo2 : [foo2]];
2727
>isArray : (arg: any) => arg is any[]
2828
>foo2 : Foo
2929
>foo2 : FooArray
30-
>[foo2] : (string | false)[]
31-
>foo2 : string | false
30+
>[foo2] : FooBase[]
31+
>foo2 : FooBase
3232

tests/baselines/reference/stringLiteralCheckedInIf02.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function f(foo: T) {
2929
>foo : T
3030

3131
return foo;
32-
>foo : "a" | "b"
32+
>foo : S
3333
}
3434
else {
3535
return foo[0];

0 commit comments

Comments
 (0)