Cherry-pick PR #32919 into release-3.6 (#32933)

Component commits:
a81ce061de Stricter criteria for eliminating types in unions during inference

f929a25407 Add regression test

6d46850172 Accept new baselines

86d9153374 Accept new API baselines

abc61a0949 Add InferencePriority.Circularity per CR feedback

ac2f151412 Accept new API baselines

c816cf2562 Add additional test

af7ccf954a Accept new baselines
This commit is contained in:
TypeScript Bot 2019-08-16 11:02:00 -07:00 committed by Ryan Cavanaugh
parent 3271a1c3cb
commit 03055d2fb6
9 changed files with 297 additions and 49 deletions

View File

@ -15486,8 +15486,7 @@ namespace ts {
let visited: Map<number>;
let bivariant = false;
let propagationType: Type;
let inferenceMatch = false;
let inferenceIncomplete = false;
let inferencePriority = InferencePriority.MaxValue;
let allowComplexConstraintInference = true;
inferFromTypes(originalSource, originalTarget);
@ -15600,7 +15599,7 @@ namespace ts {
clearCachedInferences(inferences);
}
}
inferenceMatch = true;
inferencePriority = Math.min(inferencePriority, priority);
return;
}
else {
@ -15694,19 +15693,15 @@ namespace ts {
const key = source.id + "," + target.id;
const status = visited && visited.get(key);
if (status !== undefined) {
if (status & 1) inferenceMatch = true;
if (status & 2) inferenceIncomplete = true;
inferencePriority = Math.min(inferencePriority, status);
return;
}
(visited || (visited = createMap<number>())).set(key, 0);
const saveInferenceMatch = inferenceMatch;
const saveInferenceIncomplete = inferenceIncomplete;
inferenceMatch = false;
inferenceIncomplete = false;
(visited || (visited = createMap<number>())).set(key, InferencePriority.Circularity);
const saveInferencePriority = inferencePriority;
inferencePriority = InferencePriority.MaxValue;
action(source, target);
visited.set(key, (inferenceMatch ? 1 : 0) | (inferenceIncomplete ? 2 : 0));
inferenceMatch = inferenceMatch || saveInferenceMatch;
inferenceIncomplete = inferenceIncomplete || saveInferenceIncomplete;
visited.set(key, inferencePriority);
inferencePriority = Math.min(inferencePriority, saveInferencePriority);
}
function inferFromMatchingType(source: Type, targets: Type[], matches: (s: Type, t: Type) => boolean) {
@ -15778,10 +15773,11 @@ namespace ts {
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;
let inferenceCircularity = 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.
// track whether inferences were made from that particular type to some target with
// equal priority (i.e. of equal quality) to what we would infer for a naked type
// parameter.
for (const t of targets) {
if (getInferenceInfoForType(t)) {
nakedTypeVariable = t;
@ -15789,20 +15785,20 @@ namespace ts {
}
else {
for (let i = 0; i < sources.length; i++) {
const saveInferenceMatch = inferenceMatch;
inferenceMatch = false;
const saveInferencePriority = inferencePriority;
inferencePriority = InferencePriority.MaxValue;
inferFromTypes(sources[i], t);
if (inferenceMatch) matched[i] = true;
inferenceMatch = inferenceMatch || saveInferenceMatch;
if (inferencePriority === priority) matched[i] = true;
inferenceCircularity = inferenceCircularity || inferencePriority === InferencePriority.Circularity;
inferencePriority = Math.min(inferencePriority, saveInferencePriority);
}
}
}
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) {
// If the target has a single naked type variable and no inference circularities were
// encountered above (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 && !inferenceCircularity) {
const unmatched = flatMap(sources, (s, i) => matched[i] ? undefined : s);
if (unmatched.length) {
inferFromTypes(getUnionType(unmatched), nakedTypeVariable!);
@ -15905,7 +15901,7 @@ namespace ts {
const symbol = isNonConstructorObject ? target.symbol : undefined;
if (symbol) {
if (contains(symbolStack, symbol)) {
inferenceIncomplete = true;
inferencePriority = InferencePriority.Circularity;
return;
}
(symbolStack || (symbolStack = [])).push(symbol);

View File

@ -4483,8 +4483,10 @@ namespace ts {
LiteralKeyof = 1 << 5, // Inference made from a string literal to a keyof T
NoConstraints = 1 << 6, // Don't infer from constraints of instantiable types
AlwaysStrict = 1 << 7, // Always use strict rules for contravariant inferences
MaxValue = 1 << 8, // Seed for inference priority tracking
PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates
Circularity = -1, // Inference circularity (value less than all other priorities)
}
/* @internal */

View File

@ -2441,7 +2441,9 @@ declare namespace ts {
LiteralKeyof = 32,
NoConstraints = 64,
AlwaysStrict = 128,
PriorityImpliesCombination = 56
MaxValue = 256,
PriorityImpliesCombination = 56,
Circularity = -1
}
/** @deprecated Use FileExtensionInfo instead. */
export type JsFileExtensionInfo = FileExtensionInfo;

View File

@ -2441,7 +2441,9 @@ declare namespace ts {
LiteralKeyof = 32,
NoConstraints = 64,
AlwaysStrict = 128,
PriorityImpliesCombination = 56
MaxValue = 256,
PriorityImpliesCombination = 56,
Circularity = -1
}
/** @deprecated Use FileExtensionInfo instead. */
export type JsFileExtensionInfo = FileExtensionInfo;

View File

@ -58,4 +58,30 @@ tests/cases/conformance/types/typeRelationships/typeInference/unionTypeInference
declare function bar<T>(x: T, y: string | T): T;
const y = bar(1, 2);
// Repro from #32752
const containsPromises: unique symbol = Symbol();
type DeepPromised<T> =
{ [containsPromises]?: true } &
{ [TKey in keyof T]: T[TKey] | DeepPromised<T[TKey]> | Promise<DeepPromised<T[TKey]>> };
async function fun<T>(deepPromised: DeepPromised<T>) {
const deepPromisedWithIndexer: DeepPromised<{ [name: string]: {} | null | undefined }> = deepPromised;
for (const value of Object.values(deepPromisedWithIndexer)) {
const awaitedValue = await value;
if (awaitedValue)
await fun(awaitedValue);
}
}
// Repro from #32752
type Deep<T> = { [K in keyof T]: T[K] | Deep<T[K]> };
declare function baz<T>(dp: Deep<T>): T;
declare let xx: { a: string | undefined };
baz(xx);

View File

@ -50,29 +50,64 @@ foo(x);
declare function bar<T>(x: T, y: string | T): T;
const y = bar(1, 2);
// Repro from #32752
const containsPromises: unique symbol = Symbol();
type DeepPromised<T> =
{ [containsPromises]?: true } &
{ [TKey in keyof T]: T[TKey] | DeepPromised<T[TKey]> | Promise<DeepPromised<T[TKey]>> };
async function fun<T>(deepPromised: DeepPromised<T>) {
const deepPromisedWithIndexer: DeepPromised<{ [name: string]: {} | null | undefined }> = deepPromised;
for (const value of Object.values(deepPromisedWithIndexer)) {
const awaitedValue = await value;
if (awaitedValue)
await fun(awaitedValue);
}
}
// Repro from #32752
type Deep<T> = { [K in keyof T]: T[K] | Deep<T[K]> };
declare function baz<T>(dp: Deep<T>): T;
declare let xx: { a: string | undefined };
baz(xx);
//// [unionTypeInference.js]
"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
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
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
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
const d1 = f4("abc");
const d2 = f4(s);
const d3 = f4(42); // Error
function qux(p1, p2) {
p1 = p2;
}
foo(x);
var y = bar(1, 2);
const y = bar(1, 2);
// Repro from #32752
const containsPromises = Symbol();
async function fun(deepPromised) {
const deepPromisedWithIndexer = deepPromised;
for (const value of Object.values(deepPromisedWithIndexer)) {
const awaitedValue = await value;
if (awaitedValue)
await fun(awaitedValue);
}
}
baz(xx);

View File

@ -163,12 +163,12 @@ declare function foo<T>(x: T | Promise<T>): void;
>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, --, --))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
>T : Symbol(T, Decl(unionTypeInference.ts, 45, 21))
declare let x: false | Promise<true>;
>x : Symbol(x, Decl(unionTypeInference.ts, 46, 11))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
foo(x);
>foo : Symbol(foo, Decl(unionTypeInference.ts, 41, 1))
@ -187,3 +187,92 @@ const y = bar(1, 2);
>y : Symbol(y, Decl(unionTypeInference.ts, 50, 5))
>bar : Symbol(bar, Decl(unionTypeInference.ts, 47, 7))
// Repro from #32752
const containsPromises: unique symbol = Symbol();
>containsPromises : Symbol(containsPromises, Decl(unionTypeInference.ts, 54, 5))
>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2019.symbol.d.ts, --, --))
type DeepPromised<T> =
>DeepPromised : Symbol(DeepPromised, Decl(unionTypeInference.ts, 54, 49))
>T : Symbol(T, Decl(unionTypeInference.ts, 56, 18))
{ [containsPromises]?: true } &
>[containsPromises] : Symbol([containsPromises], Decl(unionTypeInference.ts, 57, 5))
>containsPromises : Symbol(containsPromises, Decl(unionTypeInference.ts, 54, 5))
{ [TKey in keyof T]: T[TKey] | DeepPromised<T[TKey]> | Promise<DeepPromised<T[TKey]>> };
>TKey : Symbol(TKey, Decl(unionTypeInference.ts, 58, 7))
>T : Symbol(T, Decl(unionTypeInference.ts, 56, 18))
>T : Symbol(T, Decl(unionTypeInference.ts, 56, 18))
>TKey : Symbol(TKey, Decl(unionTypeInference.ts, 58, 7))
>DeepPromised : Symbol(DeepPromised, Decl(unionTypeInference.ts, 54, 49))
>T : Symbol(T, Decl(unionTypeInference.ts, 56, 18))
>TKey : Symbol(TKey, Decl(unionTypeInference.ts, 58, 7))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
>DeepPromised : Symbol(DeepPromised, Decl(unionTypeInference.ts, 54, 49))
>T : Symbol(T, Decl(unionTypeInference.ts, 56, 18))
>TKey : Symbol(TKey, Decl(unionTypeInference.ts, 58, 7))
async function fun<T>(deepPromised: DeepPromised<T>) {
>fun : Symbol(fun, Decl(unionTypeInference.ts, 58, 92))
>T : Symbol(T, Decl(unionTypeInference.ts, 60, 19))
>deepPromised : Symbol(deepPromised, Decl(unionTypeInference.ts, 60, 22))
>DeepPromised : Symbol(DeepPromised, Decl(unionTypeInference.ts, 54, 49))
>T : Symbol(T, Decl(unionTypeInference.ts, 60, 19))
const deepPromisedWithIndexer: DeepPromised<{ [name: string]: {} | null | undefined }> = deepPromised;
>deepPromisedWithIndexer : Symbol(deepPromisedWithIndexer, Decl(unionTypeInference.ts, 61, 9))
>DeepPromised : Symbol(DeepPromised, Decl(unionTypeInference.ts, 54, 49))
>name : Symbol(name, Decl(unionTypeInference.ts, 61, 51))
>deepPromised : Symbol(deepPromised, Decl(unionTypeInference.ts, 60, 22))
for (const value of Object.values(deepPromisedWithIndexer)) {
>value : Symbol(value, Decl(unionTypeInference.ts, 62, 14))
>Object.values : Symbol(ObjectConstructor.values, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>values : Symbol(ObjectConstructor.values, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --))
>deepPromisedWithIndexer : Symbol(deepPromisedWithIndexer, Decl(unionTypeInference.ts, 61, 9))
const awaitedValue = await value;
>awaitedValue : Symbol(awaitedValue, Decl(unionTypeInference.ts, 63, 13))
>value : Symbol(value, Decl(unionTypeInference.ts, 62, 14))
if (awaitedValue)
>awaitedValue : Symbol(awaitedValue, Decl(unionTypeInference.ts, 63, 13))
await fun(awaitedValue);
>fun : Symbol(fun, Decl(unionTypeInference.ts, 58, 92))
>awaitedValue : Symbol(awaitedValue, Decl(unionTypeInference.ts, 63, 13))
}
}
// Repro from #32752
type Deep<T> = { [K in keyof T]: T[K] | Deep<T[K]> };
>Deep : Symbol(Deep, Decl(unionTypeInference.ts, 67, 1))
>T : Symbol(T, Decl(unionTypeInference.ts, 71, 10))
>K : Symbol(K, Decl(unionTypeInference.ts, 71, 18))
>T : Symbol(T, Decl(unionTypeInference.ts, 71, 10))
>T : Symbol(T, Decl(unionTypeInference.ts, 71, 10))
>K : Symbol(K, Decl(unionTypeInference.ts, 71, 18))
>Deep : Symbol(Deep, Decl(unionTypeInference.ts, 67, 1))
>T : Symbol(T, Decl(unionTypeInference.ts, 71, 10))
>K : Symbol(K, Decl(unionTypeInference.ts, 71, 18))
declare function baz<T>(dp: Deep<T>): T;
>baz : Symbol(baz, Decl(unionTypeInference.ts, 71, 53))
>T : Symbol(T, Decl(unionTypeInference.ts, 73, 21))
>dp : Symbol(dp, Decl(unionTypeInference.ts, 73, 24))
>Deep : Symbol(Deep, Decl(unionTypeInference.ts, 67, 1))
>T : Symbol(T, Decl(unionTypeInference.ts, 73, 21))
>T : Symbol(T, Decl(unionTypeInference.ts, 73, 21))
declare let xx: { a: string | undefined };
>xx : Symbol(xx, Decl(unionTypeInference.ts, 74, 11))
>a : Symbol(a, Decl(unionTypeInference.ts, 74, 17))
baz(xx);
>baz : Symbol(baz, Decl(unionTypeInference.ts, 71, 53))
>xx : Symbol(xx, Decl(unionTypeInference.ts, 74, 11))

View File

@ -185,3 +185,72 @@ const y = bar(1, 2);
>1 : 1
>2 : 2
// Repro from #32752
const containsPromises: unique symbol = Symbol();
>containsPromises : unique symbol
>Symbol() : unique symbol
>Symbol : SymbolConstructor
type DeepPromised<T> =
>DeepPromised : DeepPromised<T>
{ [containsPromises]?: true } &
>[containsPromises] : true | undefined
>containsPromises : unique symbol
>true : true
{ [TKey in keyof T]: T[TKey] | DeepPromised<T[TKey]> | Promise<DeepPromised<T[TKey]>> };
async function fun<T>(deepPromised: DeepPromised<T>) {
>fun : <T>(deepPromised: DeepPromised<T>) => Promise<void>
>deepPromised : DeepPromised<T>
const deepPromisedWithIndexer: DeepPromised<{ [name: string]: {} | null | undefined }> = deepPromised;
>deepPromisedWithIndexer : DeepPromised<{ [name: string]: {} | null | undefined; }>
>name : string
>null : null
>deepPromised : DeepPromised<T>
for (const value of Object.values(deepPromisedWithIndexer)) {
>value : {} | ({ [containsPromises]?: true | undefined; } & {}) | Promise<{ [containsPromises]?: true | undefined; } & {}> | null | undefined
>Object.values(deepPromisedWithIndexer) : ({} | ({ [containsPromises]?: true | undefined; } & {}) | Promise<{ [containsPromises]?: true | undefined; } & {}> | null | undefined)[]
>Object.values : { <T>(o: { [s: string]: T; } | ArrayLike<T>): T[]; (o: {}): any[]; }
>Object : ObjectConstructor
>values : { <T>(o: { [s: string]: T; } | ArrayLike<T>): T[]; (o: {}): any[]; }
>deepPromisedWithIndexer : DeepPromised<{ [name: string]: {} | null | undefined; }>
const awaitedValue = await value;
>awaitedValue : {} | ({ [containsPromises]?: true | undefined; } & {}) | null | undefined
>await value : {} | ({ [containsPromises]?: true | undefined; } & {}) | null | undefined
>value : {} | ({ [containsPromises]?: true | undefined; } & {}) | Promise<{ [containsPromises]?: true | undefined; } & {}> | null | undefined
if (awaitedValue)
>awaitedValue : {} | ({ [containsPromises]?: true | undefined; } & {}) | null | undefined
await fun(awaitedValue);
>await fun(awaitedValue) : void
>fun(awaitedValue) : Promise<void>
>fun : <T>(deepPromised: DeepPromised<T>) => Promise<void>
>awaitedValue : {} | ({ [containsPromises]?: true | undefined; } & {})
}
}
// Repro from #32752
type Deep<T> = { [K in keyof T]: T[K] | Deep<T[K]> };
>Deep : Deep<T>
declare function baz<T>(dp: Deep<T>): T;
>baz : <T>(dp: Deep<T>) => T
>dp : Deep<T>
declare let xx: { a: string | undefined };
>xx : { a: string | undefined; }
>a : string | undefined
baz(xx);
>baz(xx) : { a: string | undefined; }
>baz : <T>(dp: Deep<T>) => T
>xx : { a: string | undefined; }

View File

@ -1,4 +1,5 @@
// @strict: true
// @target: esnext
declare const b: boolean;
declare const s: string;
@ -51,3 +52,29 @@ foo(x);
declare function bar<T>(x: T, y: string | T): T;
const y = bar(1, 2);
// Repro from #32752
const containsPromises: unique symbol = Symbol();
type DeepPromised<T> =
{ [containsPromises]?: true } &
{ [TKey in keyof T]: T[TKey] | DeepPromised<T[TKey]> | Promise<DeepPromised<T[TKey]>> };
async function fun<T>(deepPromised: DeepPromised<T>) {
const deepPromisedWithIndexer: DeepPromised<{ [name: string]: {} | null | undefined }> = deepPromised;
for (const value of Object.values(deepPromisedWithIndexer)) {
const awaitedValue = await value;
if (awaitedValue)
await fun(awaitedValue);
}
}
// Repro from #32752
type Deep<T> = { [K in keyof T]: T[K] | Deep<T[K]> };
declare function baz<T>(dp: Deep<T>): T;
declare let xx: { a: string | undefined };
baz(xx);