Skip to content

Improve type inference for types like 'T | Promise<T>' #32460

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

Merged
merged 19 commits into from
Jul 23, 2019
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
193 changes: 133 additions & 60 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

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) : string
>f1(a1) : unknown
>f1 : <T>(x: string | T) => T
>a1 : string

Expand Down
79 changes: 51 additions & 28 deletions tests/baselines/reference/unionTypeInference.errors.txt
Original file line number Diff line number Diff line change
@@ -1,38 +1,61 @@
tests/cases/conformance/types/typeRelationships/typeInference/unionTypeInference.ts(9,15): error TS2345: Argument of type '2' is not assignable to parameter of type 'string | 1'.
tests/cases/conformance/types/typeRelationships/typeInference/unionTypeInference.ts(13,24): error TS2345: Argument of type '1' is not assignable to parameter of type 'string'.
tests/cases/conformance/types/typeRelationships/typeInference/unionTypeInference.ts(31,15): error TS2345: Argument of type '42' is not assignable to parameter of type 'never'.


==== tests/cases/conformance/types/typeRelationships/typeInference/unionTypeInference.ts (1 errors) ====
// Verify that inferences made *to* a type parameter in a union type are secondary
// to inferences made directly to that type parameter
==== tests/cases/conformance/types/typeRelationships/typeInference/unionTypeInference.ts (2 errors) ====
declare const b: boolean;
declare const s: string;
declare const sn: string | number;

function f<T>(x: T, y: string|T): T {
return x;
declare function f1<T>(x: T, y: string | T): T;

const a1 = f1(1, 2); // 1 | 2
const a2 = f1(1, "hello"); // 1
const a3 = f1(1, sn); // number
const a4 = f1(undefined, "abc"); // undefined
const a5 = f1("foo", "bar"); // "foo"
const a6 = f1(true, false); // boolean
const a7 = f1("hello", 1); // Error
~
!!! error TS2345: Argument of type '1' is not assignable to parameter of type 'string'.

declare function f2<T>(value: [string, T]): T;

var b1 = f2(["string", true]); // boolean

declare function f3<T>(x: string | false | T): T;

const c1 = f3(5); // 5
const c2 = f3(sn); // number
const c3 = f3(true); // true
const c4 = f3(b); // true
const c5 = f3("abc"); // never

declare function f4<T>(x: string & T): T;

const d1 = f4("abc");
const d2 = f4(s);
const d3 = f4(42); // Error
~~
!!! error TS2345: Argument of type '42' is not assignable to parameter of type 'never'.

export interface Foo<T> {
then<U>(f: (x: T) => U | Foo<U>, g: U): Foo<U>;
}
export interface Bar<T> {
then<S>(f: (x: T) => S | Bar<S>, g: S): Bar<S>;
}

var a1: number;
var a1 = f(1, 2);
~
!!! error TS2345: Argument of type '2' is not assignable to parameter of type 'string | 1'.
var a2: number;
var a2 = f(1, "hello");
var a3: number;
var a3 = f(1, a1 || "hello");
var a4: any;
var a4 = f(undefined, "abc");

function g<T>(value: [string, T]): T {
return value[1];
function qux(p1: Foo<void>, p2: Bar<void>) {
p1 = p2;
}

var b1: boolean;
var b1 = g(["string", true]);
// Repros from #32434

function h<T>(x: string|boolean|T): T {
return typeof x === "string" || typeof x === "boolean" ? undefined : x;
}
declare function foo<T>(x: T | Promise<T>): void;
declare let x: false | Promise<true>;
foo(x);

var c1: number;
var c1 = h(5);
var c2: string;
var c2 = h("abc");
declare function bar<T>(x: T, y: string | T): T;
const y = bar(1, 2);

114 changes: 66 additions & 48 deletions tests/baselines/reference/unionTypeInference.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,78 @@
//// [unionTypeInference.ts]
// Verify that inferences made *to* a type parameter in a union type are secondary
// to inferences made directly to that type parameter
declare const b: boolean;
declare const s: string;
declare const sn: string | number;

function f<T>(x: T, y: string|T): T {
return x;
declare function f1<T>(x: T, y: string | T): T;

const a1 = f1(1, 2); // 1 | 2
const a2 = f1(1, "hello"); // 1
const a3 = f1(1, sn); // number
const a4 = f1(undefined, "abc"); // undefined
const a5 = f1("foo", "bar"); // "foo"
const a6 = f1(true, false); // boolean
const a7 = f1("hello", 1); // Error

declare function f2<T>(value: [string, T]): T;

var b1 = f2(["string", true]); // boolean

declare function f3<T>(x: string | false | T): T;

const c1 = f3(5); // 5
const c2 = f3(sn); // number
const c3 = f3(true); // true
const c4 = f3(b); // true
const c5 = f3("abc"); // never

declare function f4<T>(x: string & T): T;

const d1 = f4("abc");
const d2 = f4(s);
const d3 = f4(42); // Error

export interface Foo<T> {
then<U>(f: (x: T) => U | Foo<U>, g: U): Foo<U>;
}
export interface Bar<T> {
then<S>(f: (x: T) => S | Bar<S>, g: S): Bar<S>;
}

var a1: number;
var a1 = f(1, 2);
var a2: number;
var a2 = f(1, "hello");
var a3: number;
var a3 = f(1, a1 || "hello");
var a4: any;
var a4 = f(undefined, "abc");

function g<T>(value: [string, T]): T {
return value[1];
function qux(p1: Foo<void>, p2: Bar<void>) {
p1 = p2;
}

var b1: boolean;
var b1 = g(["string", true]);
// Repros from #32434

function h<T>(x: string|boolean|T): T {
return typeof x === "string" || typeof x === "boolean" ? undefined : x;
}
declare function foo<T>(x: T | Promise<T>): void;
declare let x: false | Promise<true>;
foo(x);

var c1: number;
var c1 = h(5);
var c2: string;
var c2 = h("abc");
declare function bar<T>(x: T, y: string | T): T;
const y = bar(1, 2);


//// [unionTypeInference.js]
// Verify that inferences made *to* a type parameter in a union type are secondary
// to inferences made directly to that type parameter
function f(x, y) {
return x;
}
var a1;
var a1 = f(1, 2);
var a2;
var a2 = f(1, "hello");
var a3;
var a3 = f(1, a1 || "hello");
var a4;
var a4 = f(undefined, "abc");
function g(value) {
return value[1];
}
var b1;
var b1 = g(["string", true]);
function h(x) {
return typeof x === "string" || typeof x === "boolean" ? undefined : x;
"use strict";
exports.__esModule = true;
var a1 = f1(1, 2); // 1 | 2
var a2 = f1(1, "hello"); // 1
var a3 = f1(1, sn); // number
var a4 = f1(undefined, "abc"); // undefined
var a5 = f1("foo", "bar"); // "foo"
var a6 = f1(true, false); // boolean
var a7 = f1("hello", 1); // Error
var b1 = f2(["string", true]); // boolean
var c1 = f3(5); // 5
var c2 = f3(sn); // number
var c3 = f3(true); // true
var c4 = f3(b); // true
var c5 = f3("abc"); // never
var d1 = f4("abc");
var d2 = f4(s);
var d3 = f4(42); // Error
function qux(p1, p2) {
p1 = p2;
}
var c1;
var c1 = h(5);
var c2;
var c2 = h("abc");
foo(x);
var y = bar(1, 2);
Loading