Handle contravariance in type inference

This commit is contained in:
Anders Hejlsberg 2017-09-18 17:31:54 -07:00
parent 670d7113da
commit a0fa69ff6c
2 changed files with 30 additions and 7 deletions

View File

@ -10129,6 +10129,10 @@ namespace ts {
getUnionType(types, /*subtypeReduction*/ true);
}
function getCommonSubtype(types: Type[]) {
return reduceLeft(types, (s, t) => isTypeSubtypeOf(t, s) ? t : s);
}
function isArrayType(type: Type): boolean {
return getObjectFlags(type) & ObjectFlags.Reference && (<TypeReference>type).target === globalArrayType;
}
@ -10655,8 +10659,14 @@ namespace ts {
const sourceTypes = (<TypeReference>source).typeArguments || emptyArray;
const targetTypes = (<TypeReference>target).typeArguments || emptyArray;
const count = sourceTypes.length < targetTypes.length ? sourceTypes.length : targetTypes.length;
const variances = strictFunctionTypes ? getVariances((<TypeReference>source).target) : undefined;
for (let i = 0; i < count; i++) {
inferFromTypes(sourceTypes[i], targetTypes[i]);
if (variances && i < variances.length && variances[i] === Variance.Contravariant) {
inferFromContravariantTypes(sourceTypes[i], targetTypes[i]);
}
else {
inferFromTypes(sourceTypes[i], targetTypes[i]);
}
}
}
else if (source.flags & TypeFlags.Index && target.flags & TypeFlags.Index) {
@ -10727,6 +10737,17 @@ namespace ts {
}
}
function inferFromContravariantTypes(source: Type, target: Type) {
if (strictFunctionTypes) {
priority ^= InferencePriority.Contravariant;
inferFromTypes(source, target);
priority ^= InferencePriority.Contravariant;
}
else {
inferFromTypes(source, target);
}
}
function getInferenceInfoForType(type: Type) {
if (type.flags & TypeFlags.TypeVariable) {
for (const inference of inferences) {
@ -10804,7 +10825,7 @@ namespace ts {
}
function inferFromSignature(source: Signature, target: Signature) {
forEachMatchingParameterType(source, target, inferFromTypes);
forEachMatchingParameterType(source, target, inferFromContravariantTypes);
if (source.typePredicate && target.typePredicate && source.typePredicate.kind === target.typePredicate.kind) {
inferFromTypes(source.typePredicate.type, target.typePredicate.type);
@ -10879,8 +10900,9 @@ namespace ts {
const baseCandidates = widenLiteralTypes ? sameMap(inference.candidates, getWidenedLiteralType) : inference.candidates;
// Infer widened union or supertype, or the unknown type for no common supertype. We infer union types
// for inferences coming from return types in order to avoid common supertype failures.
const unionOrSuperType = context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.ReturnType ?
getUnionType(baseCandidates, /*subtypeReduction*/ true) : getCommonSupertype(baseCandidates);
const unionOrSuperType = inference.priority & InferencePriority.Contravariant ? getCommonSubtype(baseCandidates) :
context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.ReturnType ? getUnionType(baseCandidates, /*subtypeReduction*/ true) :
getCommonSupertype(baseCandidates);
inferredType = getWidenedType(unionOrSuperType);
}
else if (context.flags & InferenceFlags.NoDefault) {

View File

@ -3532,9 +3532,10 @@ namespace ts {
}
export const enum InferencePriority {
NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type
MappedType = 1 << 1, // Reverse inference for mapped type
ReturnType = 1 << 2, // Inference made from return type of generic function
Contravariant = 1 << 0, // Contravariant inference
NakedTypeVariable = 1 << 1, // Naked type variable in union or intersection type
MappedType = 1 << 2, // Reverse inference for mapped type
ReturnType = 1 << 3, // Inference made from return type of generic function
}
export interface InferenceInfo {