Merge pull request #32460 from microsoft/fix32434

Improve type inference for types like 'T | Promise<T>'
This commit is contained in:
Anders Hejlsberg
2019-07-23 10:28:02 -07:00
committed by GitHub
7 changed files with 621 additions and 316 deletions

View File

@@ -13540,6 +13540,9 @@ namespace ts {
if (relation !== identityRelation) {
source = getApparentType(source);
}
else if (isGenericMappedType(source)) {
return Ternary.False;
}
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target &&
!(getObjectFlags(source) & ObjectFlags.MarkerType || getObjectFlags(target) & ObjectFlags.MarkerType)) {
// We have type references to the same generic type, and the type references are not marker
@@ -15456,9 +15459,11 @@ namespace ts {
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, contravariant = false) {
let symbolStack: Symbol[];
let visited: Map<boolean>;
let visited: Map<number>;
let bivariant = false;
let propagationType: Type;
let inferenceCount = 0;
let inferenceIncomplete = false;
let allowComplexConstraintInference = true;
inferFromTypes(originalSource, originalTarget);
@@ -15500,23 +15505,28 @@ namespace ts {
// of all their possible values.
let matchingTypes: Type[] | undefined;
for (const t of (<UnionOrIntersectionType>source).types) {
if (typeIdenticalToSomeType(t, (<UnionOrIntersectionType>target).types)) {
(matchingTypes || (matchingTypes = [])).push(t);
inferFromTypes(t, t);
}
else if (t.flags & (TypeFlags.NumberLiteral | TypeFlags.StringLiteral)) {
const b = getBaseTypeOfLiteralType(t);
if (typeIdenticalToSomeType(b, (<UnionOrIntersectionType>target).types)) {
(matchingTypes || (matchingTypes = [])).push(t, b);
}
const matched = findMatchedType(t, <UnionOrIntersectionType>target);
if (matched) {
(matchingTypes || (matchingTypes = [])).push(matched);
inferFromTypes(matched, matched);
}
}
// Next, to improve the quality of inferences, reduce the source and target types by
// removing the identically matched constituents. For example, when inferring from
// 'string | string[]' to 'string | T' we reduce the types to 'string[]' and 'T'.
if (matchingTypes) {
source = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>source, matchingTypes);
target = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>target, matchingTypes);
const s = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>source, matchingTypes);
const t = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>target, matchingTypes);
if (!(s && t)) return;
source = s;
target = t;
}
}
else if (target.flags & TypeFlags.Union && !(target.flags & TypeFlags.EnumLiteral) || target.flags & TypeFlags.Intersection) {
const matched = findMatchedType(source, <UnionOrIntersectionType>target);
if (matched) {
inferFromTypes(matched, matched);
return;
}
}
else if (target.flags & (TypeFlags.IndexedAccess | TypeFlags.Substitution)) {
@@ -15562,13 +15572,14 @@ namespace ts {
clearCachedInferences(inferences);
}
}
inferenceCount++;
return;
}
else {
// Infer to the simplified version of an indexed access, if possible, to (hopefully) expose more bare type parameters to the inference engine
const simplified = getSimplifiedType(target, /*writing*/ false);
if (simplified !== target) {
inferFromTypesOnce(source, simplified);
invokeOnce(source, simplified, inferFromTypes);
}
else if (target.flags & TypeFlags.IndexedAccess) {
const indexType = getSimplifiedType((target as IndexedAccessType).indexType, /*writing*/ false);
@@ -15577,13 +15588,14 @@ namespace ts {
if (indexType.flags & TypeFlags.Instantiable) {
const simplified = distributeIndexOverObjectType(getSimplifiedType((target as IndexedAccessType).objectType, /*writing*/ false), indexType, /*writing*/ false);
if (simplified && simplified !== target) {
inferFromTypesOnce(source, simplified);
invokeOnce(source, simplified, inferFromTypes);
}
}
}
}
}
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (
(<TypeReference>source).target === (<TypeReference>target).target || isArrayType(source) && isArrayType(target))) {
// If source and target are references to the same generic type, infer from type arguments
inferFromTypeArguments((<TypeReference>source).typeArguments || emptyArray, (<TypeReference>target).typeArguments || emptyArray, getVariances((<TypeReference>source).target));
}
@@ -15613,10 +15625,10 @@ namespace ts {
}
else if (target.flags & TypeFlags.Conditional && !contravariant) {
const targetTypes = [getTrueTypeFromConditionalType(<ConditionalType>target), getFalseTypeFromConditionalType(<ConditionalType>target)];
inferToMultipleTypes(source, targetTypes, /*isIntersection*/ false);
inferToMultipleTypes(source, targetTypes, target.flags);
}
else if (target.flags & TypeFlags.UnionOrIntersection) {
inferToMultipleTypes(source, (<UnionOrIntersectionType>target).types, !!(target.flags & TypeFlags.Intersection));
inferToMultipleTypes(source, (<UnionOrIntersectionType>target).types, target.flags);
}
else if (source.flags & TypeFlags.Union) {
// Source is a union or intersection type, infer from each constituent type
@@ -15645,39 +15657,22 @@ namespace ts {
source = apparentSource;
}
if (source.flags & (TypeFlags.Object | TypeFlags.Intersection)) {
const key = source.id + "," + target.id;
if (visited && visited.get(key)) {
return;
}
(visited || (visited = createMap<boolean>())).set(key, true);
// If we are already processing another target type with the same associated symbol (such as
// an instantiation of the same generic type), we do not explore this target as it would yield
// no further inferences. We exclude the static side of classes from this check since it shares
// its symbol with the instance side which would lead to false positives.
const isNonConstructorObject = target.flags & TypeFlags.Object &&
!(getObjectFlags(target) & ObjectFlags.Anonymous && target.symbol && target.symbol.flags & SymbolFlags.Class);
const symbol = isNonConstructorObject ? target.symbol : undefined;
if (symbol) {
if (contains(symbolStack, symbol)) {
return;
}
(symbolStack || (symbolStack = [])).push(symbol);
inferFromObjectTypes(source, target);
symbolStack.pop();
}
else {
inferFromObjectTypes(source, target);
}
invokeOnce(source, target, inferFromObjectTypes);
}
}
}
function inferFromTypesOnce(source: Type, target: Type) {
const key = source.id + "," + target.id;
if (!visited || !visited.get(key)) {
(visited || (visited = createMap<boolean>())).set(key, true);
inferFromTypes(source, target);
}
function invokeOnce(source: Type, target: Type, action: (source: Type, target: Type) => void) {
const key = source.id + "," + target.id;
const count = visited && visited.get(key);
if (count !== undefined) {
inferenceCount += count;
return;
}
(visited || (visited = createMap<number>())).set(key, 0);
const startCount = inferenceCount;
action(source, target);
visited.set(key, inferenceCount - startCount);
}
function inferFromTypeArguments(sourceTypes: readonly Type[], targetTypes: readonly Type[], variances: readonly VarianceFlags[]) {
@@ -15714,24 +15709,60 @@ namespace ts {
return undefined;
}
function inferToMultipleTypes(source: Type, targets: Type[], isIntersection: boolean) {
// We infer from types that are not naked type variables first so that inferences we
// make from nested naked type variables and given slightly higher priority by virtue
// of being first in the candidates array.
function inferToMultipleTypes(source: Type, targets: Type[], targetFlags: TypeFlags) {
let typeVariableCount = 0;
for (const t of targets) {
if (getInferenceInfoForType(t)) {
typeVariableCount++;
if (targetFlags & TypeFlags.Union) {
let nakedTypeVariable: Type | undefined;
const sources = source.flags & TypeFlags.Union ? (<UnionType>source).types : [source];
const matched = new Array<boolean>(sources.length);
const saveInferenceIncomplete = inferenceIncomplete;
inferenceIncomplete = false;
// First infer to types that are not naked type variables. For each source type we
// track whether inferences were made from that particular type to some target.
for (const t of targets) {
if (getInferenceInfoForType(t)) {
nakedTypeVariable = t;
typeVariableCount++;
}
else {
for (let i = 0; i < sources.length; i++) {
const count = inferenceCount;
inferFromTypes(sources[i], t);
if (count !== inferenceCount) matched[i] = true;
}
}
}
else {
inferFromTypes(source, t);
const inferenceComplete = !inferenceIncomplete;
inferenceIncomplete = inferenceIncomplete || saveInferenceIncomplete;
// If the target has a single naked type variable and inference completed (meaning we
// explored the types fully), create a union of the source types from which no inferences
// have been made so far and infer from that union to the naked type variable.
if (typeVariableCount === 1 && inferenceComplete) {
const unmatched = flatMap(sources, (s, i) => matched[i] ? undefined : s);
if (unmatched.length) {
inferFromTypes(getUnionType(unmatched), nakedTypeVariable!);
return;
}
}
}
else {
// We infer from types that are not naked type variables first so that inferences we
// make from nested naked type variables and given slightly higher priority by virtue
// of being first in the candidates array.
for (const t of targets) {
if (getInferenceInfoForType(t)) {
typeVariableCount++;
}
else {
inferFromTypes(source, t);
}
}
}
// Inferences directly to naked type variables are given lower priority as they are
// less specific. For example, when inferring from Promise<string> to T | Promise<T>,
// we want to infer string for T, not Promise<string> | string. For intersection types
// we only infer to single naked type variables.
if (isIntersection ? typeVariableCount === 1 : typeVariableCount !== 0) {
if (targetFlags & TypeFlags.Intersection ? typeVariableCount === 1 : typeVariableCount > 0) {
const savePriority = priority;
priority |= InferencePriority.NakedTypeVariable;
for (const t of targets) {
@@ -15800,6 +15831,28 @@ namespace ts {
}
function inferFromObjectTypes(source: Type, target: Type) {
// If we are already processing another target type with the same associated symbol (such as
// an instantiation of the same generic type), we do not explore this target as it would yield
// no further inferences. We exclude the static side of classes from this check since it shares
// its symbol with the instance side which would lead to false positives.
const isNonConstructorObject = target.flags & TypeFlags.Object &&
!(getObjectFlags(target) & ObjectFlags.Anonymous && target.symbol && target.symbol.flags & SymbolFlags.Class);
const symbol = isNonConstructorObject ? target.symbol : undefined;
if (symbol) {
if (contains(symbolStack, symbol)) {
inferenceIncomplete = true;
return;
}
(symbolStack || (symbolStack = [])).push(symbol);
inferFromObjectTypesWorker(source, target);
symbolStack.pop();
}
else {
inferFromObjectTypesWorker(source, target);
}
}
function inferFromObjectTypesWorker(source: Type, target: Type) {
if (isGenericMappedType(source) && isGenericMappedType(target)) {
// The source and target types are generic types { [P in S]: X } and { [P in T]: Y }, so we infer
// from S to T and from X to Y.
@@ -15902,15 +15955,35 @@ namespace ts {
}
}
function typeIdenticalToSomeType(type: Type, types: Type[]): boolean {
function isMatchableType(type: Type) {
// We exclude non-anonymous object types because some frameworks (e.g. Ember) rely on the ability to
// infer between types that don't witness their type variables. Such types would otherwise be eliminated
// because they appear identical.
return !(type.flags & TypeFlags.Object) || !!(getObjectFlags(type) & ObjectFlags.Anonymous);
}
function typeMatchedBySomeType(type: Type, types: Type[]): boolean {
for (const t of types) {
if (isTypeIdenticalTo(t, type)) {
if (t === type || isMatchableType(t) && isMatchableType(type) && isTypeIdenticalTo(t, type)) {
return true;
}
}
return false;
}
function findMatchedType(type: Type, target: UnionOrIntersectionType) {
if (typeMatchedBySomeType(type, target.types)) {
return type;
}
if (type.flags & (TypeFlags.NumberLiteral | TypeFlags.StringLiteral) && target.flags & TypeFlags.Union) {
const base = getBaseTypeOfLiteralType(type);
if (typeMatchedBySomeType(base, target.types)) {
return base;
}
}
return undefined;
}
/**
* Return a new union or intersection type computed by removing a given set of types
* from a given union or intersection type.
@@ -15918,11 +15991,11 @@ namespace ts {
function removeTypesFromUnionOrIntersection(type: UnionOrIntersectionType, typesToRemove: Type[]) {
const reducedTypes: Type[] = [];
for (const t of type.types) {
if (!typeIdenticalToSomeType(t, typesToRemove)) {
if (!typeMatchedBySomeType(t, typesToRemove)) {
reducedTypes.push(t);
}
}
return type.flags & TypeFlags.Union ? getUnionType(reducedTypes) : getIntersectionType(reducedTypes);
return reducedTypes.length ? type.flags & TypeFlags.Union ? getUnionType(reducedTypes) : getIntersectionType(reducedTypes) : undefined;
}
function hasPrimitiveConstraint(type: TypeParameter): boolean {

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
"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 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;
}
var c1;
var c1 = h(5);
var c2;
var c2 = h("abc");
foo(x);
var y = bar(1, 2);

View File

@@ -1,94 +1,189 @@
=== tests/cases/conformance/types/typeRelationships/typeInference/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;
>b : Symbol(b, Decl(unionTypeInference.ts, 0, 13))
function f<T>(x: T, y: string|T): T {
>f : Symbol(f, Decl(unionTypeInference.ts, 0, 0))
>T : Symbol(T, Decl(unionTypeInference.ts, 3, 11))
>x : Symbol(x, Decl(unionTypeInference.ts, 3, 14))
>T : Symbol(T, Decl(unionTypeInference.ts, 3, 11))
>y : Symbol(y, Decl(unionTypeInference.ts, 3, 19))
>T : Symbol(T, Decl(unionTypeInference.ts, 3, 11))
>T : Symbol(T, Decl(unionTypeInference.ts, 3, 11))
declare const s: string;
>s : Symbol(s, Decl(unionTypeInference.ts, 1, 13))
return x;
>x : Symbol(x, Decl(unionTypeInference.ts, 3, 14))
}
declare const sn: string | number;
>sn : Symbol(sn, Decl(unionTypeInference.ts, 2, 13))
var a1: number;
>a1 : Symbol(a1, Decl(unionTypeInference.ts, 7, 3), Decl(unionTypeInference.ts, 8, 3))
declare function f1<T>(x: T, y: string | T): T;
>f1 : Symbol(f1, Decl(unionTypeInference.ts, 2, 34))
>T : Symbol(T, Decl(unionTypeInference.ts, 4, 20))
>x : Symbol(x, Decl(unionTypeInference.ts, 4, 23))
>T : Symbol(T, Decl(unionTypeInference.ts, 4, 20))
>y : Symbol(y, Decl(unionTypeInference.ts, 4, 28))
>T : Symbol(T, Decl(unionTypeInference.ts, 4, 20))
>T : Symbol(T, Decl(unionTypeInference.ts, 4, 20))
var a1 = f(1, 2);
>a1 : Symbol(a1, Decl(unionTypeInference.ts, 7, 3), Decl(unionTypeInference.ts, 8, 3))
>f : Symbol(f, Decl(unionTypeInference.ts, 0, 0))
const a1 = f1(1, 2); // 1 | 2
>a1 : Symbol(a1, Decl(unionTypeInference.ts, 6, 5))
>f1 : Symbol(f1, Decl(unionTypeInference.ts, 2, 34))
var a2: number;
>a2 : Symbol(a2, Decl(unionTypeInference.ts, 9, 3), Decl(unionTypeInference.ts, 10, 3))
const a2 = f1(1, "hello"); // 1
>a2 : Symbol(a2, Decl(unionTypeInference.ts, 7, 5))
>f1 : Symbol(f1, Decl(unionTypeInference.ts, 2, 34))
var a2 = f(1, "hello");
>a2 : Symbol(a2, Decl(unionTypeInference.ts, 9, 3), Decl(unionTypeInference.ts, 10, 3))
>f : Symbol(f, Decl(unionTypeInference.ts, 0, 0))
const a3 = f1(1, sn); // number
>a3 : Symbol(a3, Decl(unionTypeInference.ts, 8, 5))
>f1 : Symbol(f1, Decl(unionTypeInference.ts, 2, 34))
>sn : Symbol(sn, Decl(unionTypeInference.ts, 2, 13))
var a3: number;
>a3 : Symbol(a3, Decl(unionTypeInference.ts, 11, 3), Decl(unionTypeInference.ts, 12, 3))
var a3 = f(1, a1 || "hello");
>a3 : Symbol(a3, Decl(unionTypeInference.ts, 11, 3), Decl(unionTypeInference.ts, 12, 3))
>f : Symbol(f, Decl(unionTypeInference.ts, 0, 0))
>a1 : Symbol(a1, Decl(unionTypeInference.ts, 7, 3), Decl(unionTypeInference.ts, 8, 3))
var a4: any;
>a4 : Symbol(a4, Decl(unionTypeInference.ts, 13, 3), Decl(unionTypeInference.ts, 14, 3))
var a4 = f(undefined, "abc");
>a4 : Symbol(a4, Decl(unionTypeInference.ts, 13, 3), Decl(unionTypeInference.ts, 14, 3))
>f : Symbol(f, Decl(unionTypeInference.ts, 0, 0))
const a4 = f1(undefined, "abc"); // undefined
>a4 : Symbol(a4, Decl(unionTypeInference.ts, 9, 5))
>f1 : Symbol(f1, Decl(unionTypeInference.ts, 2, 34))
>undefined : Symbol(undefined)
function g<T>(value: [string, T]): T {
>g : Symbol(g, Decl(unionTypeInference.ts, 14, 29))
>T : Symbol(T, Decl(unionTypeInference.ts, 16, 11))
>value : Symbol(value, Decl(unionTypeInference.ts, 16, 14))
>T : Symbol(T, Decl(unionTypeInference.ts, 16, 11))
>T : Symbol(T, Decl(unionTypeInference.ts, 16, 11))
const a5 = f1("foo", "bar"); // "foo"
>a5 : Symbol(a5, Decl(unionTypeInference.ts, 10, 5))
>f1 : Symbol(f1, Decl(unionTypeInference.ts, 2, 34))
return value[1];
>value : Symbol(value, Decl(unionTypeInference.ts, 16, 14))
>1 : Symbol(1)
const a6 = f1(true, false); // boolean
>a6 : Symbol(a6, Decl(unionTypeInference.ts, 11, 5))
>f1 : Symbol(f1, Decl(unionTypeInference.ts, 2, 34))
const a7 = f1("hello", 1); // Error
>a7 : Symbol(a7, Decl(unionTypeInference.ts, 12, 5))
>f1 : Symbol(f1, Decl(unionTypeInference.ts, 2, 34))
declare function f2<T>(value: [string, T]): T;
>f2 : Symbol(f2, Decl(unionTypeInference.ts, 12, 26))
>T : Symbol(T, Decl(unionTypeInference.ts, 14, 20))
>value : Symbol(value, Decl(unionTypeInference.ts, 14, 23))
>T : Symbol(T, Decl(unionTypeInference.ts, 14, 20))
>T : Symbol(T, Decl(unionTypeInference.ts, 14, 20))
var b1 = f2(["string", true]); // boolean
>b1 : Symbol(b1, Decl(unionTypeInference.ts, 16, 3))
>f2 : Symbol(f2, Decl(unionTypeInference.ts, 12, 26))
declare function f3<T>(x: string | false | T): T;
>f3 : Symbol(f3, Decl(unionTypeInference.ts, 16, 30))
>T : Symbol(T, Decl(unionTypeInference.ts, 18, 20))
>x : Symbol(x, Decl(unionTypeInference.ts, 18, 23))
>T : Symbol(T, Decl(unionTypeInference.ts, 18, 20))
>T : Symbol(T, Decl(unionTypeInference.ts, 18, 20))
const c1 = f3(5); // 5
>c1 : Symbol(c1, Decl(unionTypeInference.ts, 20, 5))
>f3 : Symbol(f3, Decl(unionTypeInference.ts, 16, 30))
const c2 = f3(sn); // number
>c2 : Symbol(c2, Decl(unionTypeInference.ts, 21, 5))
>f3 : Symbol(f3, Decl(unionTypeInference.ts, 16, 30))
>sn : Symbol(sn, Decl(unionTypeInference.ts, 2, 13))
const c3 = f3(true); // true
>c3 : Symbol(c3, Decl(unionTypeInference.ts, 22, 5))
>f3 : Symbol(f3, Decl(unionTypeInference.ts, 16, 30))
const c4 = f3(b); // true
>c4 : Symbol(c4, Decl(unionTypeInference.ts, 23, 5))
>f3 : Symbol(f3, Decl(unionTypeInference.ts, 16, 30))
>b : Symbol(b, Decl(unionTypeInference.ts, 0, 13))
const c5 = f3("abc"); // never
>c5 : Symbol(c5, Decl(unionTypeInference.ts, 24, 5))
>f3 : Symbol(f3, Decl(unionTypeInference.ts, 16, 30))
declare function f4<T>(x: string & T): T;
>f4 : Symbol(f4, Decl(unionTypeInference.ts, 24, 21))
>T : Symbol(T, Decl(unionTypeInference.ts, 26, 20))
>x : Symbol(x, Decl(unionTypeInference.ts, 26, 23))
>T : Symbol(T, Decl(unionTypeInference.ts, 26, 20))
>T : Symbol(T, Decl(unionTypeInference.ts, 26, 20))
const d1 = f4("abc");
>d1 : Symbol(d1, Decl(unionTypeInference.ts, 28, 5))
>f4 : Symbol(f4, Decl(unionTypeInference.ts, 24, 21))
const d2 = f4(s);
>d2 : Symbol(d2, Decl(unionTypeInference.ts, 29, 5))
>f4 : Symbol(f4, Decl(unionTypeInference.ts, 24, 21))
>s : Symbol(s, Decl(unionTypeInference.ts, 1, 13))
const d3 = f4(42); // Error
>d3 : Symbol(d3, Decl(unionTypeInference.ts, 30, 5))
>f4 : Symbol(f4, Decl(unionTypeInference.ts, 24, 21))
export interface Foo<T> {
>Foo : Symbol(Foo, Decl(unionTypeInference.ts, 30, 18))
>T : Symbol(T, Decl(unionTypeInference.ts, 32, 21))
then<U>(f: (x: T) => U | Foo<U>, g: U): Foo<U>;
>then : Symbol(Foo.then, Decl(unionTypeInference.ts, 32, 25))
>U : Symbol(U, Decl(unionTypeInference.ts, 33, 9))
>f : Symbol(f, Decl(unionTypeInference.ts, 33, 12))
>x : Symbol(x, Decl(unionTypeInference.ts, 33, 16))
>T : Symbol(T, Decl(unionTypeInference.ts, 32, 21))
>U : Symbol(U, Decl(unionTypeInference.ts, 33, 9))
>Foo : Symbol(Foo, Decl(unionTypeInference.ts, 30, 18))
>U : Symbol(U, Decl(unionTypeInference.ts, 33, 9))
>g : Symbol(g, Decl(unionTypeInference.ts, 33, 36))
>U : Symbol(U, Decl(unionTypeInference.ts, 33, 9))
>Foo : Symbol(Foo, Decl(unionTypeInference.ts, 30, 18))
>U : Symbol(U, Decl(unionTypeInference.ts, 33, 9))
}
export interface Bar<T> {
>Bar : Symbol(Bar, Decl(unionTypeInference.ts, 34, 1))
>T : Symbol(T, Decl(unionTypeInference.ts, 35, 21))
then<S>(f: (x: T) => S | Bar<S>, g: S): Bar<S>;
>then : Symbol(Bar.then, Decl(unionTypeInference.ts, 35, 25))
>S : Symbol(S, Decl(unionTypeInference.ts, 36, 9))
>f : Symbol(f, Decl(unionTypeInference.ts, 36, 12))
>x : Symbol(x, Decl(unionTypeInference.ts, 36, 16))
>T : Symbol(T, Decl(unionTypeInference.ts, 35, 21))
>S : Symbol(S, Decl(unionTypeInference.ts, 36, 9))
>Bar : Symbol(Bar, Decl(unionTypeInference.ts, 34, 1))
>S : Symbol(S, Decl(unionTypeInference.ts, 36, 9))
>g : Symbol(g, Decl(unionTypeInference.ts, 36, 36))
>S : Symbol(S, Decl(unionTypeInference.ts, 36, 9))
>Bar : Symbol(Bar, Decl(unionTypeInference.ts, 34, 1))
>S : Symbol(S, Decl(unionTypeInference.ts, 36, 9))
}
var b1: boolean;
>b1 : Symbol(b1, Decl(unionTypeInference.ts, 20, 3), Decl(unionTypeInference.ts, 21, 3))
function qux(p1: Foo<void>, p2: Bar<void>) {
>qux : Symbol(qux, Decl(unionTypeInference.ts, 37, 1))
>p1 : Symbol(p1, Decl(unionTypeInference.ts, 39, 13))
>Foo : Symbol(Foo, Decl(unionTypeInference.ts, 30, 18))
>p2 : Symbol(p2, Decl(unionTypeInference.ts, 39, 27))
>Bar : Symbol(Bar, Decl(unionTypeInference.ts, 34, 1))
var b1 = g(["string", true]);
>b1 : Symbol(b1, Decl(unionTypeInference.ts, 20, 3), Decl(unionTypeInference.ts, 21, 3))
>g : Symbol(g, Decl(unionTypeInference.ts, 14, 29))
function h<T>(x: string|boolean|T): T {
>h : Symbol(h, Decl(unionTypeInference.ts, 21, 29))
>T : Symbol(T, Decl(unionTypeInference.ts, 23, 11))
>x : Symbol(x, Decl(unionTypeInference.ts, 23, 14))
>T : Symbol(T, Decl(unionTypeInference.ts, 23, 11))
>T : Symbol(T, Decl(unionTypeInference.ts, 23, 11))
return typeof x === "string" || typeof x === "boolean" ? undefined : x;
>x : Symbol(x, Decl(unionTypeInference.ts, 23, 14))
>x : Symbol(x, Decl(unionTypeInference.ts, 23, 14))
>undefined : Symbol(undefined)
>x : Symbol(x, Decl(unionTypeInference.ts, 23, 14))
p1 = p2;
>p1 : Symbol(p1, Decl(unionTypeInference.ts, 39, 13))
>p2 : Symbol(p2, Decl(unionTypeInference.ts, 39, 27))
}
var c1: number;
>c1 : Symbol(c1, Decl(unionTypeInference.ts, 27, 3), Decl(unionTypeInference.ts, 28, 3))
// Repros from #32434
var c1 = h(5);
>c1 : Symbol(c1, Decl(unionTypeInference.ts, 27, 3), Decl(unionTypeInference.ts, 28, 3))
>h : Symbol(h, Decl(unionTypeInference.ts, 21, 29))
declare function foo<T>(x: T | Promise<T>): void;
>foo : Symbol(foo, Decl(unionTypeInference.ts, 41, 1))
>T : Symbol(T, Decl(unionTypeInference.ts, 45, 21))
>x : Symbol(x, Decl(unionTypeInference.ts, 45, 24))
>T : Symbol(T, Decl(unionTypeInference.ts, 45, 21))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(unionTypeInference.ts, 45, 21))
var c2: string;
>c2 : Symbol(c2, Decl(unionTypeInference.ts, 29, 3), Decl(unionTypeInference.ts, 30, 3))
declare let x: false | Promise<true>;
>x : Symbol(x, Decl(unionTypeInference.ts, 46, 11))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --))
var c2 = h("abc");
>c2 : Symbol(c2, Decl(unionTypeInference.ts, 29, 3), Decl(unionTypeInference.ts, 30, 3))
>h : Symbol(h, Decl(unionTypeInference.ts, 21, 29))
foo(x);
>foo : Symbol(foo, Decl(unionTypeInference.ts, 41, 1))
>x : Symbol(x, Decl(unionTypeInference.ts, 46, 11))
declare function bar<T>(x: T, y: string | T): T;
>bar : Symbol(bar, Decl(unionTypeInference.ts, 47, 7))
>T : Symbol(T, Decl(unionTypeInference.ts, 49, 21))
>x : Symbol(x, Decl(unionTypeInference.ts, 49, 24))
>T : Symbol(T, Decl(unionTypeInference.ts, 49, 21))
>y : Symbol(y, Decl(unionTypeInference.ts, 49, 29))
>T : Symbol(T, Decl(unionTypeInference.ts, 49, 21))
>T : Symbol(T, Decl(unionTypeInference.ts, 49, 21))
const y = bar(1, 2);
>y : Symbol(y, Decl(unionTypeInference.ts, 50, 5))
>bar : Symbol(bar, Decl(unionTypeInference.ts, 47, 7))

View File

@@ -1,113 +1,187 @@
=== tests/cases/conformance/types/typeRelationships/typeInference/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;
>b : boolean
function f<T>(x: T, y: string|T): T {
>f : <T>(x: T, y: string | T) => T
declare const s: string;
>s : string
declare const sn: string | number;
>sn : string | number
declare function f1<T>(x: T, y: string | T): T;
>f1 : <T>(x: T, y: string | T) => T
>x : T
>y : string | T
return x;
>x : T
}
var a1: number;
>a1 : number
var a1 = f(1, 2);
>a1 : number
>f(1, 2) : any
>f : <T>(x: T, y: string | T) => T
const a1 = f1(1, 2); // 1 | 2
>a1 : 1 | 2
>f1(1, 2) : 1 | 2
>f1 : <T>(x: T, y: string | T) => T
>1 : 1
>2 : 2
var a2: number;
>a2 : number
var a2 = f(1, "hello");
>a2 : number
>f(1, "hello") : 1
>f : <T>(x: T, y: string | T) => T
const a2 = f1(1, "hello"); // 1
>a2 : 1
>f1(1, "hello") : 1
>f1 : <T>(x: T, y: string | T) => T
>1 : 1
>"hello" : "hello"
var a3: number;
const a3 = f1(1, sn); // number
>a3 : number
var a3 = f(1, a1 || "hello");
>a3 : number
>f(1, a1 || "hello") : number
>f : <T>(x: T, y: string | T) => T
>f1(1, sn) : number
>f1 : <T>(x: T, y: string | T) => T
>1 : 1
>a1 || "hello" : number | "hello"
>a1 : number
>"hello" : "hello"
>sn : string | number
var a4: any;
>a4 : any
var a4 = f(undefined, "abc");
>a4 : any
>f(undefined, "abc") : any
>f : <T>(x: T, y: string | T) => T
const a4 = f1(undefined, "abc"); // undefined
>a4 : undefined
>f1(undefined, "abc") : undefined
>f1 : <T>(x: T, y: string | T) => T
>undefined : undefined
>"abc" : "abc"
function g<T>(value: [string, T]): T {
>g : <T>(value: [string, T]) => T
>value : [string, T]
const a5 = f1("foo", "bar"); // "foo"
>a5 : "foo"
>f1("foo", "bar") : "foo"
>f1 : <T>(x: T, y: string | T) => T
>"foo" : "foo"
>"bar" : "bar"
return value[1];
>value[1] : T
>value : [string, T]
const a6 = f1(true, false); // boolean
>a6 : boolean
>f1(true, false) : boolean
>f1 : <T>(x: T, y: string | T) => T
>true : true
>false : false
const a7 = f1("hello", 1); // Error
>a7 : any
>f1("hello", 1) : any
>f1 : <T>(x: T, y: string | T) => T
>"hello" : "hello"
>1 : 1
}
var b1: boolean;
>b1 : boolean
declare function f2<T>(value: [string, T]): T;
>f2 : <T>(value: [string, T]) => T
>value : [string, T]
var b1 = g(["string", true]);
var b1 = f2(["string", true]); // boolean
>b1 : boolean
>g(["string", true]) : boolean
>g : <T>(value: [string, T]) => T
>f2(["string", true]) : boolean
>f2 : <T>(value: [string, T]) => T
>["string", true] : [string, true]
>"string" : "string"
>true : true
function h<T>(x: string|boolean|T): T {
>h : <T>(x: string | boolean | T) => T
>x : string | boolean | T
declare function f3<T>(x: string | false | T): T;
>f3 : <T>(x: string | false | T) => T
>x : string | false | T
>false : false
return typeof x === "string" || typeof x === "boolean" ? undefined : x;
>typeof x === "string" || typeof x === "boolean" ? undefined : x : T
>typeof x === "string" || typeof x === "boolean" : boolean
>typeof x === "string" : boolean
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : string | boolean | T
>"string" : "string"
>typeof x === "boolean" : boolean
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : boolean | T
>"boolean" : "boolean"
>undefined : undefined
>x : T
}
var c1: number;
>c1 : number
var c1 = h(5);
>c1 : number
>h(5) : 5
>h : <T>(x: string | boolean | T) => T
const c1 = f3(5); // 5
>c1 : 5
>f3(5) : 5
>f3 : <T>(x: string | false | T) => T
>5 : 5
var c2: string;
>c2 : string
const c2 = f3(sn); // number
>c2 : number
>f3(sn) : number
>f3 : <T>(x: string | false | T) => T
>sn : string | number
var c2 = h("abc");
>c2 : string
>h("abc") : "abc"
>h : <T>(x: string | boolean | T) => T
const c3 = f3(true); // true
>c3 : true
>f3(true) : true
>f3 : <T>(x: string | false | T) => T
>true : true
const c4 = f3(b); // true
>c4 : true
>f3(b) : true
>f3 : <T>(x: string | false | T) => T
>b : boolean
const c5 = f3("abc"); // never
>c5 : unknown
>f3("abc") : unknown
>f3 : <T>(x: string | false | T) => T
>"abc" : "abc"
declare function f4<T>(x: string & T): T;
>f4 : <T>(x: string & T) => T
>x : string & T
const d1 = f4("abc");
>d1 : "abc"
>f4("abc") : "abc"
>f4 : <T>(x: string & T) => T
>"abc" : "abc"
const d2 = f4(s);
>d2 : unknown
>f4(s) : unknown
>f4 : <T>(x: string & T) => T
>s : string
const d3 = f4(42); // Error
>d3 : any
>f4(42) : any
>f4 : <T>(x: string & T) => T
>42 : 42
export interface Foo<T> {
then<U>(f: (x: T) => U | Foo<U>, g: U): Foo<U>;
>then : <U>(f: (x: T) => U | Foo<U>, g: U) => Foo<U>
>f : (x: T) => U | Foo<U>
>x : T
>g : U
}
export interface Bar<T> {
then<S>(f: (x: T) => S | Bar<S>, g: S): Bar<S>;
>then : <S>(f: (x: T) => S | Bar<S>, g: S) => Bar<S>
>f : (x: T) => S | Bar<S>
>x : T
>g : S
}
function qux(p1: Foo<void>, p2: Bar<void>) {
>qux : (p1: Foo<void>, p2: Bar<void>) => void
>p1 : Foo<void>
>p2 : Bar<void>
p1 = p2;
>p1 = p2 : Bar<void>
>p1 : Foo<void>
>p2 : Bar<void>
}
// Repros from #32434
declare function foo<T>(x: T | Promise<T>): void;
>foo : <T>(x: T | Promise<T>) => void
>x : T | Promise<T>
declare let x: false | Promise<true>;
>x : false | Promise<true>
>false : false
>true : true
foo(x);
>foo(x) : void
>foo : <T>(x: T | Promise<T>) => void
>x : false | Promise<true>
declare function bar<T>(x: T, y: string | T): T;
>bar : <T>(x: T, y: string | T) => T
>x : T
>y : string | T
const y = bar(1, 2);
>y : 1 | 2
>bar(1, 2) : 1 | 2
>bar : <T>(x: T, y: string | T) => T
>1 : 1
>2 : 2

View File

@@ -1,31 +1,53 @@
// Verify that inferences made *to* a type parameter in a union type are secondary
// to inferences made directly to that type parameter
// @strict: true
function f<T>(x: T, y: string|T): T {
return x;
declare const b: boolean;
declare const s: string;
declare const sn: string | number;
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);