Merge pull request #33252 from microsoft/fix33131
Make lower priority inference when source is completely matched in target
This commit is contained in:
commit
c5e6d95e93
|
@ -15593,24 +15593,30 @@ namespace ts {
|
|||
return;
|
||||
}
|
||||
if (target.flags & TypeFlags.Union) {
|
||||
if (source.flags & TypeFlags.Union) {
|
||||
// First, infer between identically matching source and target constituents and remove the
|
||||
// matching types.
|
||||
const [tempSources, tempTargets] = inferFromMatchingTypes((<UnionType>source).types, (<UnionType>target).types, isTypeOrBaseIdenticalTo);
|
||||
// Next, infer between closely matching source and target constituents and remove
|
||||
// the matching types. Types closely match when they are instantiations of the same
|
||||
// object type or instantiations of the same type alias.
|
||||
const [sources, targets] = inferFromMatchingTypes(tempSources, tempTargets, isTypeCloselyMatchedBy);
|
||||
if (sources.length === 0 || targets.length === 0) {
|
||||
return;
|
||||
}
|
||||
source = getUnionType(sources);
|
||||
target = getUnionType(targets);
|
||||
// First, infer between identically matching source and target constituents and remove the
|
||||
// matching types.
|
||||
const [tempSources, tempTargets] = inferFromMatchingTypes(source.flags & TypeFlags.Union ? (<UnionType>source).types : [source], (<UnionType>target).types, isTypeOrBaseIdenticalTo);
|
||||
// Next, infer between closely matching source and target constituents and remove
|
||||
// the matching types. Types closely match when they are instantiations of the same
|
||||
// object type or instantiations of the same type alias.
|
||||
const [sources, targets] = inferFromMatchingTypes(tempSources, tempTargets, isTypeCloselyMatchedBy);
|
||||
if (targets.length === 0) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (inferFromMatchingType(source, (<UnionType>target).types, isTypeOrBaseIdenticalTo)) return;
|
||||
if (inferFromMatchingType(source, (<UnionType>target).types, isTypeCloselyMatchedBy)) return;
|
||||
target = getUnionType(targets);
|
||||
if (sources.length === 0) {
|
||||
// All source constituents have been matched and there is nothing further to infer from.
|
||||
// However, simply making no inferences is undesirable because it could ultimately mean
|
||||
// inferring a type parameter constraint. Instead, make a lower priority inference from
|
||||
// the full source to whatever remains in the target. For example, when inferring from
|
||||
// string to 'string | T', make a lower priority inference of string for T.
|
||||
const savePriority = priority;
|
||||
priority |= InferencePriority.NakedTypeVariable;
|
||||
inferFromTypes(source, target);
|
||||
priority = savePriority;
|
||||
return;
|
||||
}
|
||||
source = getUnionType(sources);
|
||||
}
|
||||
else if (target.flags & TypeFlags.Intersection && some((<IntersectionType>target).types,
|
||||
t => !!getInferenceInfoForType(t) || (isGenericMappedType(t) && !!getInferenceInfoForType(getHomomorphicTypeVariable(t) || neverType)))) {
|
||||
|
@ -15620,18 +15626,15 @@ namespace ts {
|
|||
// string[] on the source side and infer string for T.
|
||||
// Likewise, we consider a homomorphic mapped type constrainted to the target type parameter as similar to a "naked type variable"
|
||||
// in such scenarios.
|
||||
if (source.flags & TypeFlags.Intersection) {
|
||||
if (!(source.flags & TypeFlags.Union)) {
|
||||
// Infer between identically matching source and target constituents and remove the matching types.
|
||||
const [sources, targets] = inferFromMatchingTypes((<IntersectionType>source).types, (<IntersectionType>target).types, isTypeIdenticalTo);
|
||||
const [sources, targets] = inferFromMatchingTypes(source.flags & TypeFlags.Intersection ? (<IntersectionType>source).types : [source], (<IntersectionType>target).types, isTypeIdenticalTo);
|
||||
if (sources.length === 0 || targets.length === 0) {
|
||||
return;
|
||||
}
|
||||
source = getIntersectionType(sources);
|
||||
target = getIntersectionType(targets);
|
||||
}
|
||||
else if (!(source.flags & TypeFlags.Union)) {
|
||||
if (inferFromMatchingType(source, (<IntersectionType>target).types, isTypeIdenticalTo)) return;
|
||||
}
|
||||
}
|
||||
else if (target.flags & (TypeFlags.IndexedAccess | TypeFlags.Substitution)) {
|
||||
target = getActualTypeVariable(target);
|
||||
|
@ -15781,17 +15784,6 @@ namespace ts {
|
|||
inferencePriority = Math.min(inferencePriority, saveInferencePriority);
|
||||
}
|
||||
|
||||
function inferFromMatchingType(source: Type, targets: Type[], matches: (s: Type, t: Type) => boolean) {
|
||||
let matched = false;
|
||||
for (const t of targets) {
|
||||
if (matches(source, t)) {
|
||||
inferFromTypes(source, t);
|
||||
matched = true;
|
||||
}
|
||||
}
|
||||
return matched;
|
||||
}
|
||||
|
||||
function inferFromMatchingTypes(sources: Type[], targets: Type[], matches: (s: Type, t: Type) => boolean): [Type[], Type[]] {
|
||||
let matchedSources: Type[] | undefined;
|
||||
let matchedTargets: Type[] | undefined;
|
||||
|
|
29
tests/baselines/reference/observableInferenceCanBeMade.js
Normal file
29
tests/baselines/reference/observableInferenceCanBeMade.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
//// [observableInferenceCanBeMade.ts]
|
||||
// Repro from #33131
|
||||
|
||||
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]
|
||||
"use strict";
|
||||
// Repro from #33131
|
||||
function asObservable(input) {
|
||||
return typeof input === 'string' ? of(input) : from(input);
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
=== tests/cases/compiler/observableInferenceCanBeMade.ts ===
|
||||
// Repro from #33131
|
||||
|
||||
declare function of<T>(a: T): Observable<T>;
|
||||
>of : Symbol(of, Decl(observableInferenceCanBeMade.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 2, 20))
|
||||
>a : Symbol(a, Decl(observableInferenceCanBeMade.ts, 2, 23))
|
||||
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 2, 20))
|
||||
>Observable : Symbol(Observable, Decl(observableInferenceCanBeMade.ts, 10, 64))
|
||||
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 2, 20))
|
||||
|
||||
declare function from<O extends ObservableInput<any>>(input: O): Observable<ObservedValueOf<O>>;
|
||||
>from : Symbol(from, Decl(observableInferenceCanBeMade.ts, 2, 44))
|
||||
>O : Symbol(O, Decl(observableInferenceCanBeMade.ts, 3, 22))
|
||||
>ObservableInput : Symbol(ObservableInput, Decl(observableInferenceCanBeMade.ts, 9, 1))
|
||||
>input : Symbol(input, Decl(observableInferenceCanBeMade.ts, 3, 54))
|
||||
>O : Symbol(O, Decl(observableInferenceCanBeMade.ts, 3, 22))
|
||||
>Observable : Symbol(Observable, Decl(observableInferenceCanBeMade.ts, 10, 64))
|
||||
>ObservedValueOf : Symbol(ObservedValueOf, Decl(observableInferenceCanBeMade.ts, 3, 96))
|
||||
>O : Symbol(O, Decl(observableInferenceCanBeMade.ts, 3, 22))
|
||||
|
||||
type ObservedValueOf<O> = O extends ObservableInput<infer T> ? T : never;
|
||||
>ObservedValueOf : Symbol(ObservedValueOf, Decl(observableInferenceCanBeMade.ts, 3, 96))
|
||||
>O : Symbol(O, Decl(observableInferenceCanBeMade.ts, 5, 21))
|
||||
>O : Symbol(O, Decl(observableInferenceCanBeMade.ts, 5, 21))
|
||||
>ObservableInput : Symbol(ObservableInput, Decl(observableInferenceCanBeMade.ts, 9, 1))
|
||||
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 5, 57))
|
||||
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 5, 57))
|
||||
|
||||
interface Subscribable<T> {
|
||||
>Subscribable : Symbol(Subscribable, Decl(observableInferenceCanBeMade.ts, 5, 73))
|
||||
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 7, 23))
|
||||
|
||||
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void;
|
||||
>subscribe : Symbol(Subscribable.subscribe, Decl(observableInferenceCanBeMade.ts, 7, 27))
|
||||
>next : Symbol(next, Decl(observableInferenceCanBeMade.ts, 8, 14))
|
||||
>value : Symbol(value, Decl(observableInferenceCanBeMade.ts, 8, 22))
|
||||
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 7, 23))
|
||||
>error : Symbol(error, Decl(observableInferenceCanBeMade.ts, 8, 40))
|
||||
>error : Symbol(error, Decl(observableInferenceCanBeMade.ts, 8, 50))
|
||||
>complete : Symbol(complete, Decl(observableInferenceCanBeMade.ts, 8, 70))
|
||||
}
|
||||
type ObservableInput<T> = Subscribable<T> | Subscribable<never>;
|
||||
>ObservableInput : Symbol(ObservableInput, Decl(observableInferenceCanBeMade.ts, 9, 1))
|
||||
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 10, 21))
|
||||
>Subscribable : Symbol(Subscribable, Decl(observableInferenceCanBeMade.ts, 5, 73))
|
||||
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 10, 21))
|
||||
>Subscribable : Symbol(Subscribable, Decl(observableInferenceCanBeMade.ts, 5, 73))
|
||||
|
||||
|
||||
declare class Observable<T> implements Subscribable<T> {
|
||||
>Observable : Symbol(Observable, Decl(observableInferenceCanBeMade.ts, 10, 64))
|
||||
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 13, 25))
|
||||
>Subscribable : Symbol(Subscribable, Decl(observableInferenceCanBeMade.ts, 5, 73))
|
||||
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 13, 25))
|
||||
|
||||
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void;
|
||||
>subscribe : Symbol(Observable.subscribe, Decl(observableInferenceCanBeMade.ts, 13, 56))
|
||||
>next : Symbol(next, Decl(observableInferenceCanBeMade.ts, 14, 14))
|
||||
>value : Symbol(value, Decl(observableInferenceCanBeMade.ts, 14, 22))
|
||||
>T : Symbol(T, Decl(observableInferenceCanBeMade.ts, 13, 25))
|
||||
>error : Symbol(error, Decl(observableInferenceCanBeMade.ts, 14, 40))
|
||||
>error : Symbol(error, Decl(observableInferenceCanBeMade.ts, 14, 50))
|
||||
>complete : Symbol(complete, Decl(observableInferenceCanBeMade.ts, 14, 70))
|
||||
}
|
||||
|
||||
function asObservable(input: string | ObservableInput<string>): Observable<string> {
|
||||
>asObservable : Symbol(asObservable, Decl(observableInferenceCanBeMade.ts, 15, 1))
|
||||
>input : Symbol(input, Decl(observableInferenceCanBeMade.ts, 17, 22))
|
||||
>ObservableInput : Symbol(ObservableInput, Decl(observableInferenceCanBeMade.ts, 9, 1))
|
||||
>Observable : Symbol(Observable, Decl(observableInferenceCanBeMade.ts, 10, 64))
|
||||
|
||||
return typeof input === 'string' ? of(input) : from(input)
|
||||
>input : Symbol(input, Decl(observableInferenceCanBeMade.ts, 17, 22))
|
||||
>of : Symbol(of, Decl(observableInferenceCanBeMade.ts, 0, 0))
|
||||
>input : Symbol(input, Decl(observableInferenceCanBeMade.ts, 17, 22))
|
||||
>from : Symbol(from, Decl(observableInferenceCanBeMade.ts, 2, 44))
|
||||
>input : Symbol(input, Decl(observableInferenceCanBeMade.ts, 17, 22))
|
||||
}
|
||||
|
57
tests/baselines/reference/observableInferenceCanBeMade.types
Normal file
57
tests/baselines/reference/observableInferenceCanBeMade.types
Normal file
|
@ -0,0 +1,57 @@
|
|||
=== tests/cases/compiler/observableInferenceCanBeMade.ts ===
|
||||
// Repro from #33131
|
||||
|
||||
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) | undefined, error?: ((error: any) => void) | undefined, complete?: (() => void) | undefined) => void
|
||||
>next : ((value: T) => void) | undefined
|
||||
>value : T
|
||||
>error : ((error: any) => void) | undefined
|
||||
>error : any
|
||||
>complete : (() => void) | undefined
|
||||
}
|
||||
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) | undefined, error?: ((error: any) => void) | undefined, complete?: (() => void) | undefined) => void
|
||||
>next : ((value: T) => void) | undefined
|
||||
>value : T
|
||||
>error : ((error: any) => void) | undefined
|
||||
>error : any
|
||||
>complete : (() => void) | undefined
|
||||
}
|
||||
|
||||
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>
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
22
tests/cases/compiler/observableInferenceCanBeMade.ts
Normal file
22
tests/cases/compiler/observableInferenceCanBeMade.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
// @strict: true
|
||||
|
||||
// Repro from #33131
|
||||
|
||||
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)
|
||||
}
|
Loading…
Reference in a new issue