From 737d1e7b643067ca8e204b245e524f7bef453f5a Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 29 Aug 2019 16:17:21 -0700 Subject: [PATCH] Only skip following inference steps after union member matching if an inference is actually made --- src/compiler/checker.ts | 11 ++- .../reference/observableInferenceCanBeMade.js | 24 ++++++ .../observableInferenceCanBeMade.symbols | 77 +++++++++++++++++++ .../observableInferenceCanBeMade.types | 54 +++++++++++++ .../unionAndIntersectionInference2.types | 2 +- .../reference/unionTypeInference.types | 12 +-- .../compiler/observableInferenceCanBeMade.ts | 18 +++++ .../TypeScript-React-Starter | 2 +- .../user/create-react-app/create-react-app | 2 +- tests/cases/user/prettier/prettier | 2 +- tests/cases/user/puppeteer/puppeteer | 2 +- tests/cases/user/webpack/webpack | 2 +- 12 files changed, 192 insertions(+), 16 deletions(-) create mode 100644 tests/baselines/reference/observableInferenceCanBeMade.js create mode 100644 tests/baselines/reference/observableInferenceCanBeMade.symbols create mode 100644 tests/baselines/reference/observableInferenceCanBeMade.types create mode 100644 tests/cases/compiler/observableInferenceCanBeMade.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cfad47b65336f..181a70a8dff4c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15583,8 +15583,8 @@ namespace ts { target = getUnionType(targets); } else { - if (inferFromMatchingType(source, (target).types, isTypeOrBaseIdenticalTo)) return; - if (inferFromMatchingType(source, (target).types, isTypeCloselyMatchedBy)) return; + if (inferFromMatchingType(source, (target).types, isTypeOrBaseIdenticalTo, /*continueOnNoInference*/ true)) return; + if (inferFromMatchingType(source, (target).types, isTypeCloselyMatchedBy, /*continueOnNoInference*/ true)) return; } } else if (target.flags & TypeFlags.Intersection && some((target).types, t => !!getInferenceInfoForType(t))) { @@ -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; diff --git a/tests/baselines/reference/observableInferenceCanBeMade.js b/tests/baselines/reference/observableInferenceCanBeMade.js new file mode 100644 index 0000000000000..786855a2107be --- /dev/null +++ b/tests/baselines/reference/observableInferenceCanBeMade.js @@ -0,0 +1,24 @@ +//// [observableInferenceCanBeMade.ts] +declare function of(a: T): Observable; +declare function from>(input: O): Observable>; + +type ObservedValueOf = O extends ObservableInput ? T : never; + +interface Subscribable { + subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void; +} +type ObservableInput = Subscribable | Subscribable; + + +declare class Observable implements Subscribable { + subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void; +} + +function asObservable(input: string | ObservableInput): Observable { + return typeof input === 'string' ? of(input) : from(input) +} + +//// [observableInferenceCanBeMade.js] +function asObservable(input) { + return typeof input === 'string' ? of(input) : from(input); +} diff --git a/tests/baselines/reference/observableInferenceCanBeMade.symbols b/tests/baselines/reference/observableInferenceCanBeMade.symbols new file mode 100644 index 0000000000000..d3345410aac17 --- /dev/null +++ b/tests/baselines/reference/observableInferenceCanBeMade.symbols @@ -0,0 +1,77 @@ +=== tests/cases/compiler/observableInferenceCanBeMade.ts === +declare function of(a: T): Observable; +>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>(input: O): Observable>; +>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 extends ObservableInput ? 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 { +>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 = Subscribable | Subscribable; +>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 implements Subscribable { +>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): Observable { +>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)) +} diff --git a/tests/baselines/reference/observableInferenceCanBeMade.types b/tests/baselines/reference/observableInferenceCanBeMade.types new file mode 100644 index 0000000000000..beefd3eb58894 --- /dev/null +++ b/tests/baselines/reference/observableInferenceCanBeMade.types @@ -0,0 +1,54 @@ +=== tests/cases/compiler/observableInferenceCanBeMade.ts === +declare function of(a: T): Observable; +>of : (a: T) => Observable +>a : T + +declare function from>(input: O): Observable>; +>from : >(input: O) => Observable> +>input : O + +type ObservedValueOf = O extends ObservableInput ? T : never; +>ObservedValueOf : ObservedValueOf + +interface Subscribable { + 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 = Subscribable | Subscribable; +>ObservableInput : ObservableInput + + +declare class Observable implements Subscribable { +>Observable : Observable + + 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): Observable { +>asObservable : (input: string | Subscribable | Subscribable) => Observable +>input : string | Subscribable | Subscribable + + return typeof input === 'string' ? of(input) : from(input) +>typeof input === 'string' ? of(input) : from(input) : Observable +>typeof input === 'string' : boolean +>typeof input : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>input : string | Subscribable | Subscribable +>'string' : "string" +>of(input) : Observable +>of : (a: T) => Observable +>input : string +>from(input) : Observable +>from : >(input: O) => Observable> +>input : ObservableInput +} diff --git a/tests/baselines/reference/unionAndIntersectionInference2.types b/tests/baselines/reference/unionAndIntersectionInference2.types index 24f24220bd972..a163b25ce2891 100644 --- a/tests/baselines/reference/unionAndIntersectionInference2.types +++ b/tests/baselines/reference/unionAndIntersectionInference2.types @@ -20,7 +20,7 @@ var e1: number | string | boolean; >e1 : string | number | boolean f1(a1); // string ->f1(a1) : unknown +>f1(a1) : string >f1 : (x: string | T) => T >a1 : string diff --git a/tests/baselines/reference/unionTypeInference.types b/tests/baselines/reference/unionTypeInference.types index 4ce2d9c43a08c..fc553273ae52e 100644 --- a/tests/baselines/reference/unionTypeInference.types +++ b/tests/baselines/reference/unionTypeInference.types @@ -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 : (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 : (x: T, y: string | T) => T >"foo" : "foo" >"bar" : "bar" @@ -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 : (x: string | false | T) => T >"abc" : "abc" diff --git a/tests/cases/compiler/observableInferenceCanBeMade.ts b/tests/cases/compiler/observableInferenceCanBeMade.ts new file mode 100644 index 0000000000000..df8735667d83c --- /dev/null +++ b/tests/cases/compiler/observableInferenceCanBeMade.ts @@ -0,0 +1,18 @@ +declare function of(a: T): Observable; +declare function from>(input: O): Observable>; + +type ObservedValueOf = O extends ObservableInput ? T : never; + +interface Subscribable { + subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void; +} +type ObservableInput = Subscribable | Subscribable; + + +declare class Observable implements Subscribable { + subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): void; +} + +function asObservable(input: string | ObservableInput): Observable { + return typeof input === 'string' ? of(input) : from(input) +} \ No newline at end of file diff --git a/tests/cases/user/TypeScript-React-Starter/TypeScript-React-Starter b/tests/cases/user/TypeScript-React-Starter/TypeScript-React-Starter index 19c71f2c6a2b8..bfc20b2f17c02 160000 --- a/tests/cases/user/TypeScript-React-Starter/TypeScript-React-Starter +++ b/tests/cases/user/TypeScript-React-Starter/TypeScript-React-Starter @@ -1 +1 @@ -Subproject commit 19c71f2c6a2b874b1b2bb28a8526b19185b8eece +Subproject commit bfc20b2f17c0206105e2cdd42cd35d79dd03a884 diff --git a/tests/cases/user/create-react-app/create-react-app b/tests/cases/user/create-react-app/create-react-app index 6560858398ddc..437b83f0337a5 160000 --- a/tests/cases/user/create-react-app/create-react-app +++ b/tests/cases/user/create-react-app/create-react-app @@ -1 +1 @@ -Subproject commit 6560858398ddc8d1c5b8d7f51929fcb3d9c3055c +Subproject commit 437b83f0337a5d57ce7dd976d2c3b44cb2037e45 diff --git a/tests/cases/user/prettier/prettier b/tests/cases/user/prettier/prettier index 2f40dba3177c6..2314640485001 160000 --- a/tests/cases/user/prettier/prettier +++ b/tests/cases/user/prettier/prettier @@ -1 +1 @@ -Subproject commit 2f40dba3177c6edd3ceb88b26cdf4718e892a3e5 +Subproject commit 23146404850011972f695fb6bc2b8113c3cffbfc diff --git a/tests/cases/user/puppeteer/puppeteer b/tests/cases/user/puppeteer/puppeteer index cba0f98a2ac7e..b6b29502eb6a7 160000 --- a/tests/cases/user/puppeteer/puppeteer +++ b/tests/cases/user/puppeteer/puppeteer @@ -1 +1 @@ -Subproject commit cba0f98a2ac7edd3c2bffd0ac53185877403da6b +Subproject commit b6b29502eb6a75fe3869806f0e7b27195fe51b0d diff --git a/tests/cases/user/webpack/webpack b/tests/cases/user/webpack/webpack index b16ca509d12fa..743ae6da9a6fc 160000 --- a/tests/cases/user/webpack/webpack +++ b/tests/cases/user/webpack/webpack @@ -1 +1 @@ -Subproject commit b16ca509d12faf36573b65fffcbae50c5b3e7ee3 +Subproject commit 743ae6da9a6fc3b459a7ab3bb250fb07d14f9c5d