Skip to content

Only skip following inference steps after union member matching if an inference is actually made #33154

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

Closed
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
11 changes: 7 additions & 4 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15583,8 +15583,8 @@ namespace ts {
target = getUnionType(targets);
}
else {
if (inferFromMatchingType(source, (<UnionType>target).types, isTypeOrBaseIdenticalTo)) return;
if (inferFromMatchingType(source, (<UnionType>target).types, isTypeCloselyMatchedBy)) return;
if (inferFromMatchingType(source, (<UnionType>target).types, isTypeOrBaseIdenticalTo, /*continueOnNoInference*/ true)) return;
if (inferFromMatchingType(source, (<UnionType>target).types, isTypeCloselyMatchedBy, /*continueOnNoInference*/ true)) return;
}
}
else if (target.flags & TypeFlags.Intersection && some((<IntersectionType>target).types, t => !!getInferenceInfoForType(t))) {
Expand Down Expand Up @@ -15753,12 +15753,15 @@ namespace ts {
inferencePriority = Math.min(inferencePriority, saveInferencePriority);
}

function inferFromMatchingType(source: Type, targets: Type[], matches: (s: Type, t: Type) => boolean) {
function inferFromMatchingType(source: Type, targets: Type[], matches: (s: Type, t: Type) => boolean, continueOnNoInference?: boolean) {
let matched = false;
for (const t of targets) {
if (matches(source, t)) {
const currPri = inferencePriority;
inferFromTypes(source, t);
matched = true;
if (!continueOnNoInference || inferencePriority < currPri) {
matched = true;
}
}
}
return matched;
Expand Down
24 changes: 24 additions & 0 deletions tests/baselines/reference/observableInferenceCanBeMade.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//// [observableInferenceCanBeMade.ts]
declare function of<T>(a: T): Observable<T>;
declare function from<O extends ObservableInput<any>>(input: O): Observable<ObservedValueOf<O>>;

type ObservedValueOf<O> = O extends ObservableInput<infer T> ? T : never;

interface Subscribable<T> {
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void;
}
type ObservableInput<T> = Subscribable<T> | Subscribable<never>;


declare class Observable<T> implements Subscribable<T> {
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void;
}

function asObservable(input: string | ObservableInput<string>): Observable<string> {
return typeof input === 'string' ? of(input) : from(input)
}

//// [observableInferenceCanBeMade.js]
function asObservable(input) {
return typeof input === 'string' ? of(input) : from(input);
}
77 changes: 77 additions & 0 deletions tests/baselines/reference/observableInferenceCanBeMade.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
=== tests/cases/compiler/observableInferenceCanBeMade.ts ===
declare function of<T>(a: T): Observable<T>;
>of : Symbol(of, Decl(observableInferenceCanBeMade.ts, 0, 0))
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 0, 20))
>a : Symbol(a, Decl(observableInferenceCanBeMade.ts, 0, 23))
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 0, 20))
>Observable : Symbol(Observable, Decl(observableInferenceCanBeMade.ts, 8, 64))
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 0, 20))

declare function from<O extends ObservableInput<any>>(input: O): Observable<ObservedValueOf<O>>;
>from : Symbol(from, Decl(observableInferenceCanBeMade.ts, 0, 44))
>O : Symbol(O, Decl(observableInferenceCanBeMade.ts, 1, 22))
>ObservableInput : Symbol(ObservableInput, Decl(observableInferenceCanBeMade.ts, 7, 1))
>input : Symbol(input, Decl(observableInferenceCanBeMade.ts, 1, 54))
>O : Symbol(O, Decl(observableInferenceCanBeMade.ts, 1, 22))
>Observable : Symbol(Observable, Decl(observableInferenceCanBeMade.ts, 8, 64))
>ObservedValueOf : Symbol(ObservedValueOf, Decl(observableInferenceCanBeMade.ts, 1, 96))
>O : Symbol(O, Decl(observableInferenceCanBeMade.ts, 1, 22))

type ObservedValueOf<O> = O extends ObservableInput<infer T> ? T : never;
>ObservedValueOf : Symbol(ObservedValueOf, Decl(observableInferenceCanBeMade.ts, 1, 96))
>O : Symbol(O, Decl(observableInferenceCanBeMade.ts, 3, 21))
>O : Symbol(O, Decl(observableInferenceCanBeMade.ts, 3, 21))
>ObservableInput : Symbol(ObservableInput, Decl(observableInferenceCanBeMade.ts, 7, 1))
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 3, 57))
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 3, 57))

interface Subscribable<T> {
>Subscribable : Symbol(Subscribable, Decl(observableInferenceCanBeMade.ts, 3, 73))
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 5, 23))

subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void;
>subscribe : Symbol(Subscribable.subscribe, Decl(observableInferenceCanBeMade.ts, 5, 27))
>next : Symbol(next, Decl(observableInferenceCanBeMade.ts, 6, 14))
>value : Symbol(value, Decl(observableInferenceCanBeMade.ts, 6, 22))
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 5, 23))
>error : Symbol(error, Decl(observableInferenceCanBeMade.ts, 6, 40))
>error : Symbol(error, Decl(observableInferenceCanBeMade.ts, 6, 50))
>complete : Symbol(complete, Decl(observableInferenceCanBeMade.ts, 6, 70))
}
type ObservableInput<T> = Subscribable<T> | Subscribable<never>;
>ObservableInput : Symbol(ObservableInput, Decl(observableInferenceCanBeMade.ts, 7, 1))
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 8, 21))
>Subscribable : Symbol(Subscribable, Decl(observableInferenceCanBeMade.ts, 3, 73))
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 8, 21))
>Subscribable : Symbol(Subscribable, Decl(observableInferenceCanBeMade.ts, 3, 73))


declare class Observable<T> implements Subscribable<T> {
>Observable : Symbol(Observable, Decl(observableInferenceCanBeMade.ts, 8, 64))
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 11, 25))
>Subscribable : Symbol(Subscribable, Decl(observableInferenceCanBeMade.ts, 3, 73))
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 11, 25))

subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void;
>subscribe : Symbol(Observable.subscribe, Decl(observableInferenceCanBeMade.ts, 11, 56))
>next : Symbol(next, Decl(observableInferenceCanBeMade.ts, 12, 14))
>value : Symbol(value, Decl(observableInferenceCanBeMade.ts, 12, 22))
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 11, 25))
>error : Symbol(error, Decl(observableInferenceCanBeMade.ts, 12, 40))
>error : Symbol(error, Decl(observableInferenceCanBeMade.ts, 12, 50))
>complete : Symbol(complete, Decl(observableInferenceCanBeMade.ts, 12, 70))
}

function asObservable(input: string | ObservableInput<string>): Observable<string> {
>asObservable : Symbol(asObservable, Decl(observableInferenceCanBeMade.ts, 13, 1))
>input : Symbol(input, Decl(observableInferenceCanBeMade.ts, 15, 22))
>ObservableInput : Symbol(ObservableInput, Decl(observableInferenceCanBeMade.ts, 7, 1))
>Observable : Symbol(Observable, Decl(observableInferenceCanBeMade.ts, 8, 64))

return typeof input === 'string' ? of(input) : from(input)
>input : Symbol(input, Decl(observableInferenceCanBeMade.ts, 15, 22))
>of : Symbol(of, Decl(observableInferenceCanBeMade.ts, 0, 0))
>input : Symbol(input, Decl(observableInferenceCanBeMade.ts, 15, 22))
>from : Symbol(from, Decl(observableInferenceCanBeMade.ts, 0, 44))
>input : Symbol(input, Decl(observableInferenceCanBeMade.ts, 15, 22))
}
54 changes: 54 additions & 0 deletions tests/baselines/reference/observableInferenceCanBeMade.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
=== tests/cases/compiler/observableInferenceCanBeMade.ts ===
declare function of<T>(a: T): Observable<T>;
>of : <T>(a: T) => Observable<T>
>a : T

declare function from<O extends ObservableInput<any>>(input: O): Observable<ObservedValueOf<O>>;
>from : <O extends ObservableInput<any>>(input: O) => Observable<ObservedValueOf<O>>
>input : O

type ObservedValueOf<O> = O extends ObservableInput<infer T> ? T : never;
>ObservedValueOf : ObservedValueOf<O>

interface Subscribable<T> {
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void;
>subscribe : (next?: (value: T) => void, error?: (error: any) => void, complete?: () => void) => void
>next : (value: T) => void
>value : T
>error : (error: any) => void
>error : any
>complete : () => void
}
type ObservableInput<T> = Subscribable<T> | Subscribable<never>;
>ObservableInput : ObservableInput<T>


declare class Observable<T> implements Subscribable<T> {
>Observable : Observable<T>

subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void;
>subscribe : (next?: (value: T) => void, error?: (error: any) => void, complete?: () => void) => void
>next : (value: T) => void
>value : T
>error : (error: any) => void
>error : any
>complete : () => void
}

function asObservable(input: string | ObservableInput<string>): Observable<string> {
>asObservable : (input: string | Subscribable<never> | Subscribable<string>) => Observable<string>
>input : string | Subscribable<never> | Subscribable<string>

return typeof input === 'string' ? of(input) : from(input)
>typeof input === 'string' ? of(input) : from(input) : Observable<string>
>typeof input === 'string' : boolean
>typeof input : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>input : string | Subscribable<never> | Subscribable<string>
>'string' : "string"
>of(input) : Observable<string>
>of : <T>(a: T) => Observable<T>
>input : string
>from(input) : Observable<string>
>from : <O extends ObservableInput<any>>(input: O) => Observable<ObservedValueOf<O>>
>input : ObservableInput<string>
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var e1: number | string | boolean;
>e1 : string | number | boolean

f1(a1); // string
>f1(a1) : unknown
>f1(a1) : string
>f1 : <T>(x: string | T) => T
>a1 : string

Expand Down
12 changes: 6 additions & 6 deletions tests/baselines/reference/unionTypeInference.types
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ const a3 = f1(1, sn); // number
>sn : string | number

const a4 = f1(undefined, "abc"); // undefined
>a4 : undefined
>f1(undefined, "abc") : undefined
>a4 : "abc" | undefined
>f1(undefined, "abc") : "abc" | undefined
>f1 : <T>(x: T, y: string | T) => T
>undefined : undefined
>"abc" : "abc"

const a5 = f1("foo", "bar"); // "foo"
>a5 : "foo"
>f1("foo", "bar") : "foo"
>a5 : "foo" | "bar"
>f1("foo", "bar") : "foo" | "bar"
>f1 : <T>(x: T, y: string | T) => T
>"foo" : "foo"
>"bar" : "bar"
Expand Down Expand Up @@ -104,8 +104,8 @@ const c4 = f3(b); // true
>b : boolean

const c5 = f3("abc"); // never
>c5 : unknown
>f3("abc") : unknown
>c5 : "abc"
>f3("abc") : "abc"
>f3 : <T>(x: string | false | T) => T
>"abc" : "abc"

Expand Down
18 changes: 18 additions & 0 deletions tests/cases/compiler/observableInferenceCanBeMade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
declare function of<T>(a: T): Observable<T>;
declare function from<O extends ObservableInput<any>>(input: O): Observable<ObservedValueOf<O>>;

type ObservedValueOf<O> = O extends ObservableInput<infer T> ? T : never;

interface Subscribable<T> {
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void;
}
type ObservableInput<T> = Subscribable<T> | Subscribable<never>;


declare class Observable<T> implements Subscribable<T> {
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void;
}

function asObservable(input: string | ObservableInput<string>): Observable<string> {
return typeof input === 'string' ? of(input) : from(input)
}
2 changes: 1 addition & 1 deletion tests/cases/user/puppeteer/puppeteer