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 */