Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filter origin types when filtering union types #42378

Merged
merged 2 commits into from
Jan 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21689,7 +21689,27 @@ namespace ts {
if (type.flags & TypeFlags.Union) {
const types = (<UnionType>type).types;
const filtered = filter(types, f);
return filtered === types ? type : getUnionTypeFromSortedList(filtered, (<UnionType>type).objectFlags);
if (filtered === types) {
return type;
}
const origin = (<UnionType>type).origin;
let newOrigin: Type | undefined;
if (origin && origin.flags & TypeFlags.Union) {
// If the origin type is a (denormalized) union type, filter its non-union constituents. If that ends
// up removing a smaller number of types than in the normalized constituent set (meaning some of the
Copy link
Member

@DanielRosenwasser DanielRosenwasser Jan 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This says smaller, but below you're checking for whether or not you've reduced the same number of elements.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's either going to be smaller or the same. It could never be larger.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess what I'm saying is that the code is written in an inverted way, so it wasn't as obvious what's happening. I just realized it was written this way to share the same return path though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createOriginUnionOrIntersectionType is the thing you can't use if you end up removing a smaller number of types, right? If the length diff is the same, then you can, otherwise you have to fall back to getUnionTypeFromSortedList, which will ignore the origin information.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, github didn't load the previous 3 comments before I commented. I guess the point is that I think the comment is not confusing, even if the code itself is.

// filtered types are within nested unions in the origin), then we can't construct a new origin type.
// Otherwise, if we have exactly one type left in the origin set, return that as the filtered type.
// Otherwise, construct a new filtered origin type.
const originTypes = (<UnionType>origin).types;
const originFiltered = filter(originTypes, t => !!(t.flags & TypeFlags.Union) || f(t));
if (originTypes.length - originFiltered.length === types.length - filtered.length) {
if (originFiltered.length === 1) {
return originFiltered[0];
}
newOrigin = createOriginUnionOrIntersectionType(TypeFlags.Union, originFiltered);
}
}
return getUnionTypeFromSortedList(filtered, (<UnionType>type).objectFlags, /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, newOrigin);
}
return type.flags & TypeFlags.Never || f(type) ? type : neverType;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts(7,9): error TS7027: Unreachable code detected.
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.
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.


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

4 changes: 2 additions & 2 deletions tests/baselines/reference/exhaustiveSwitchStatements1.types
Original file line number Diff line number Diff line change
Expand Up @@ -695,12 +695,12 @@ function ff(o: O, k: K) {
}
k === 'c'; // Error
>k === 'c' : boolean
>k : "a" | "b"
>k : keyof O
>'c' : "c"

return o[k];
>o[k] : number
>o : O
>k : "a" | "b"
>k : keyof O
}

Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ namespace jsx {
>css : string

export type LibraryManagedAttributes<C, P> = WithCSSProp<P>
>LibraryManagedAttributes : WithCSSProp<P>
>LibraryManagedAttributes : LibraryManagedAttributes<C, P>

}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@ function asObservable(input: string | ObservableInput<string>): Observable<strin
>input : string
>from(input) : Observable<string>
>from : <O extends ObservableInput<any>>(input: O) => Observable<ObservedValueOf<O>>
>input : Subscribable<never> | Subscribable<string>
>input : ObservableInput<string>
}

Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ function fail(s: Shapes) {
if (s.kind === "circle") {
>s.kind === "circle" : boolean
>s.kind : "square" | "circle"
>s : Square | Circle
>s : Shape
>kind : "square" | "circle"
>"circle" : "circle"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ foo1 = [...Array.isArray(foo2) ? foo2 : [foo2]];
>isArray : (arg: any) => arg is any[]
>foo2 : Foo
>foo2 : FooArray
>[foo2] : (string | false)[]
>foo2 : string | false
>[foo2] : FooBase[]
>foo2 : FooBase

2 changes: 1 addition & 1 deletion tests/baselines/reference/stringLiteralCheckedInIf02.types
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ function f(foo: T) {
>foo : T

return foo;
>foo : "a" | "b"
>foo : S
}
else {
return foo[0];
Expand Down