Skip to content

Commit 737d1e7

Browse files
committed
Only skip following inference steps after union member matching if an inference is actually made
1 parent 029f7a3 commit 737d1e7

12 files changed

+192
-16
lines changed

src/compiler/checker.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -15583,8 +15583,8 @@ namespace ts {
1558315583
target = getUnionType(targets);
1558415584
}
1558515585
else {
15586-
if (inferFromMatchingType(source, (<UnionType>target).types, isTypeOrBaseIdenticalTo)) return;
15587-
if (inferFromMatchingType(source, (<UnionType>target).types, isTypeCloselyMatchedBy)) return;
15586+
if (inferFromMatchingType(source, (<UnionType>target).types, isTypeOrBaseIdenticalTo, /*continueOnNoInference*/ true)) return;
15587+
if (inferFromMatchingType(source, (<UnionType>target).types, isTypeCloselyMatchedBy, /*continueOnNoInference*/ true)) return;
1558815588
}
1558915589
}
1559015590
else if (target.flags & TypeFlags.Intersection && some((<IntersectionType>target).types, t => !!getInferenceInfoForType(t))) {
@@ -15753,12 +15753,15 @@ namespace ts {
1575315753
inferencePriority = Math.min(inferencePriority, saveInferencePriority);
1575415754
}
1575515755

15756-
function inferFromMatchingType(source: Type, targets: Type[], matches: (s: Type, t: Type) => boolean) {
15756+
function inferFromMatchingType(source: Type, targets: Type[], matches: (s: Type, t: Type) => boolean, continueOnNoInference?: boolean) {
1575715757
let matched = false;
1575815758
for (const t of targets) {
1575915759
if (matches(source, t)) {
15760+
const currPri = inferencePriority;
1576015761
inferFromTypes(source, t);
15761-
matched = true;
15762+
if (!continueOnNoInference || inferencePriority < currPri) {
15763+
matched = true;
15764+
}
1576215765
}
1576315766
}
1576415767
return matched;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//// [observableInferenceCanBeMade.ts]
2+
declare function of<T>(a: T): Observable<T>;
3+
declare function from<O extends ObservableInput<any>>(input: O): Observable<ObservedValueOf<O>>;
4+
5+
type ObservedValueOf<O> = O extends ObservableInput<infer T> ? T : never;
6+
7+
interface Subscribable<T> {
8+
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void;
9+
}
10+
type ObservableInput<T> = Subscribable<T> | Subscribable<never>;
11+
12+
13+
declare class Observable<T> implements Subscribable<T> {
14+
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void;
15+
}
16+
17+
function asObservable(input: string | ObservableInput<string>): Observable<string> {
18+
return typeof input === 'string' ? of(input) : from(input)
19+
}
20+
21+
//// [observableInferenceCanBeMade.js]
22+
function asObservable(input) {
23+
return typeof input === 'string' ? of(input) : from(input);
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
=== tests/cases/compiler/observableInferenceCanBeMade.ts ===
2+
declare function of<T>(a: T): Observable<T>;
3+
>of : Symbol(of, Decl(observableInferenceCanBeMade.ts, 0, 0))
4+
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 0, 20))
5+
>a : Symbol(a, Decl(observableInferenceCanBeMade.ts, 0, 23))
6+
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 0, 20))
7+
>Observable : Symbol(Observable, Decl(observableInferenceCanBeMade.ts, 8, 64))
8+
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 0, 20))
9+
10+
declare function from<O extends ObservableInput<any>>(input: O): Observable<ObservedValueOf<O>>;
11+
>from : Symbol(from, Decl(observableInferenceCanBeMade.ts, 0, 44))
12+
>O : Symbol(O, Decl(observableInferenceCanBeMade.ts, 1, 22))
13+
>ObservableInput : Symbol(ObservableInput, Decl(observableInferenceCanBeMade.ts, 7, 1))
14+
>input : Symbol(input, Decl(observableInferenceCanBeMade.ts, 1, 54))
15+
>O : Symbol(O, Decl(observableInferenceCanBeMade.ts, 1, 22))
16+
>Observable : Symbol(Observable, Decl(observableInferenceCanBeMade.ts, 8, 64))
17+
>ObservedValueOf : Symbol(ObservedValueOf, Decl(observableInferenceCanBeMade.ts, 1, 96))
18+
>O : Symbol(O, Decl(observableInferenceCanBeMade.ts, 1, 22))
19+
20+
type ObservedValueOf<O> = O extends ObservableInput<infer T> ? T : never;
21+
>ObservedValueOf : Symbol(ObservedValueOf, Decl(observableInferenceCanBeMade.ts, 1, 96))
22+
>O : Symbol(O, Decl(observableInferenceCanBeMade.ts, 3, 21))
23+
>O : Symbol(O, Decl(observableInferenceCanBeMade.ts, 3, 21))
24+
>ObservableInput : Symbol(ObservableInput, Decl(observableInferenceCanBeMade.ts, 7, 1))
25+
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 3, 57))
26+
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 3, 57))
27+
28+
interface Subscribable<T> {
29+
>Subscribable : Symbol(Subscribable, Decl(observableInferenceCanBeMade.ts, 3, 73))
30+
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 5, 23))
31+
32+
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void;
33+
>subscribe : Symbol(Subscribable.subscribe, Decl(observableInferenceCanBeMade.ts, 5, 27))
34+
>next : Symbol(next, Decl(observableInferenceCanBeMade.ts, 6, 14))
35+
>value : Symbol(value, Decl(observableInferenceCanBeMade.ts, 6, 22))
36+
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 5, 23))
37+
>error : Symbol(error, Decl(observableInferenceCanBeMade.ts, 6, 40))
38+
>error : Symbol(error, Decl(observableInferenceCanBeMade.ts, 6, 50))
39+
>complete : Symbol(complete, Decl(observableInferenceCanBeMade.ts, 6, 70))
40+
}
41+
type ObservableInput<T> = Subscribable<T> | Subscribable<never>;
42+
>ObservableInput : Symbol(ObservableInput, Decl(observableInferenceCanBeMade.ts, 7, 1))
43+
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 8, 21))
44+
>Subscribable : Symbol(Subscribable, Decl(observableInferenceCanBeMade.ts, 3, 73))
45+
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 8, 21))
46+
>Subscribable : Symbol(Subscribable, Decl(observableInferenceCanBeMade.ts, 3, 73))
47+
48+
49+
declare class Observable<T> implements Subscribable<T> {
50+
>Observable : Symbol(Observable, Decl(observableInferenceCanBeMade.ts, 8, 64))
51+
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 11, 25))
52+
>Subscribable : Symbol(Subscribable, Decl(observableInferenceCanBeMade.ts, 3, 73))
53+
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 11, 25))
54+
55+
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void;
56+
>subscribe : Symbol(Observable.subscribe, Decl(observableInferenceCanBeMade.ts, 11, 56))
57+
>next : Symbol(next, Decl(observableInferenceCanBeMade.ts, 12, 14))
58+
>value : Symbol(value, Decl(observableInferenceCanBeMade.ts, 12, 22))
59+
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 11, 25))
60+
>error : Symbol(error, Decl(observableInferenceCanBeMade.ts, 12, 40))
61+
>error : Symbol(error, Decl(observableInferenceCanBeMade.ts, 12, 50))
62+
>complete : Symbol(complete, Decl(observableInferenceCanBeMade.ts, 12, 70))
63+
}
64+
65+
function asObservable(input: string | ObservableInput<string>): Observable<string> {
66+
>asObservable : Symbol(asObservable, Decl(observableInferenceCanBeMade.ts, 13, 1))
67+
>input : Symbol(input, Decl(observableInferenceCanBeMade.ts, 15, 22))
68+
>ObservableInput : Symbol(ObservableInput, Decl(observableInferenceCanBeMade.ts, 7, 1))
69+
>Observable : Symbol(Observable, Decl(observableInferenceCanBeMade.ts, 8, 64))
70+
71+
return typeof input === 'string' ? of(input) : from(input)
72+
>input : Symbol(input, Decl(observableInferenceCanBeMade.ts, 15, 22))
73+
>of : Symbol(of, Decl(observableInferenceCanBeMade.ts, 0, 0))
74+
>input : Symbol(input, Decl(observableInferenceCanBeMade.ts, 15, 22))
75+
>from : Symbol(from, Decl(observableInferenceCanBeMade.ts, 0, 44))
76+
>input : Symbol(input, Decl(observableInferenceCanBeMade.ts, 15, 22))
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
=== tests/cases/compiler/observableInferenceCanBeMade.ts ===
2+
declare function of<T>(a: T): Observable<T>;
3+
>of : <T>(a: T) => Observable<T>
4+
>a : T
5+
6+
declare function from<O extends ObservableInput<any>>(input: O): Observable<ObservedValueOf<O>>;
7+
>from : <O extends ObservableInput<any>>(input: O) => Observable<ObservedValueOf<O>>
8+
>input : O
9+
10+
type ObservedValueOf<O> = O extends ObservableInput<infer T> ? T : never;
11+
>ObservedValueOf : ObservedValueOf<O>
12+
13+
interface Subscribable<T> {
14+
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void;
15+
>subscribe : (next?: (value: T) => void, error?: (error: any) => void, complete?: () => void) => void
16+
>next : (value: T) => void
17+
>value : T
18+
>error : (error: any) => void
19+
>error : any
20+
>complete : () => void
21+
}
22+
type ObservableInput<T> = Subscribable<T> | Subscribable<never>;
23+
>ObservableInput : ObservableInput<T>
24+
25+
26+
declare class Observable<T> implements Subscribable<T> {
27+
>Observable : Observable<T>
28+
29+
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void;
30+
>subscribe : (next?: (value: T) => void, error?: (error: any) => void, complete?: () => void) => void
31+
>next : (value: T) => void
32+
>value : T
33+
>error : (error: any) => void
34+
>error : any
35+
>complete : () => void
36+
}
37+
38+
function asObservable(input: string | ObservableInput<string>): Observable<string> {
39+
>asObservable : (input: string | Subscribable<never> | Subscribable<string>) => Observable<string>
40+
>input : string | Subscribable<never> | Subscribable<string>
41+
42+
return typeof input === 'string' ? of(input) : from(input)
43+
>typeof input === 'string' ? of(input) : from(input) : Observable<string>
44+
>typeof input === 'string' : boolean
45+
>typeof input : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
46+
>input : string | Subscribable<never> | Subscribable<string>
47+
>'string' : "string"
48+
>of(input) : Observable<string>
49+
>of : <T>(a: T) => Observable<T>
50+
>input : string
51+
>from(input) : Observable<string>
52+
>from : <O extends ObservableInput<any>>(input: O) => Observable<ObservedValueOf<O>>
53+
>input : ObservableInput<string>
54+
}

tests/baselines/reference/unionAndIntersectionInference2.types

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ var e1: number | string | boolean;
2020
>e1 : string | number | boolean
2121

2222
f1(a1); // string
23-
>f1(a1) : unknown
23+
>f1(a1) : string
2424
>f1 : <T>(x: string | T) => T
2525
>a1 : string
2626

tests/baselines/reference/unionTypeInference.types

+6-6
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ const a3 = f1(1, sn); // number
3535
>sn : string | number
3636

3737
const a4 = f1(undefined, "abc"); // undefined
38-
>a4 : undefined
39-
>f1(undefined, "abc") : undefined
38+
>a4 : "abc" | undefined
39+
>f1(undefined, "abc") : "abc" | undefined
4040
>f1 : <T>(x: T, y: string | T) => T
4141
>undefined : undefined
4242
>"abc" : "abc"
4343

4444
const a5 = f1("foo", "bar"); // "foo"
45-
>a5 : "foo"
46-
>f1("foo", "bar") : "foo"
45+
>a5 : "foo" | "bar"
46+
>f1("foo", "bar") : "foo" | "bar"
4747
>f1 : <T>(x: T, y: string | T) => T
4848
>"foo" : "foo"
4949
>"bar" : "bar"
@@ -104,8 +104,8 @@ const c4 = f3(b); // true
104104
>b : boolean
105105

106106
const c5 = f3("abc"); // never
107-
>c5 : unknown
108-
>f3("abc") : unknown
107+
>c5 : "abc"
108+
>f3("abc") : "abc"
109109
>f3 : <T>(x: string | false | T) => T
110110
>"abc" : "abc"
111111

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
declare function of<T>(a: T): Observable<T>;
2+
declare function from<O extends ObservableInput<any>>(input: O): Observable<ObservedValueOf<O>>;
3+
4+
type ObservedValueOf<O> = O extends ObservableInput<infer T> ? T : never;
5+
6+
interface Subscribable<T> {
7+
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void;
8+
}
9+
type ObservableInput<T> = Subscribable<T> | Subscribable<never>;
10+
11+
12+
declare class Observable<T> implements Subscribable<T> {
13+
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void;
14+
}
15+
16+
function asObservable(input: string | ObservableInput<string>): Observable<string> {
17+
return typeof input === 'string' ? of(input) : from(input)
18+
}

0 commit comments

Comments
 (0)