Skip to content

Commit

Permalink
Eliminate redundant or meaningless elaborations in type relations
Browse files Browse the repository at this point in the history
  • Loading branch information
ahejlsberg committed Feb 4, 2022
1 parent 880e2c0 commit 628da10
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 46 deletions.
104 changes: 58 additions & 46 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11823,6 +11823,16 @@ namespace ts {
return hasNonCircularBaseConstraint(type) ? getConstraintFromConditionalType(type) : undefined;
}

function hasStructuredOrInstantiableConstraint(type: Type) {
const constraint = type.flags & TypeFlags.Instantiable ? getConstraintOfType(type) : undefined;
return constraint && !!(constraint.flags & TypeFlags.StructuredOrInstantiable);
}

function isUnionContainingMultipleObjectLikeTypes(type: Type) {
return !!(type.flags & TypeFlags.Union) &&
countWhere((type as UnionType).types, t => !!(t.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.Substitution))) >= 2;
}

function getEffectiveConstraintOfIntersection(types: readonly Type[], targetIsUnion: boolean) {
let constraints: Type[] | undefined;
let hasDisjointDomainType = false;
Expand Down Expand Up @@ -18346,18 +18356,27 @@ namespace ts {
let result = Ternary.False;
const saveErrorInfo = captureErrorCalculationState();

if ((source.flags & TypeFlags.Union || target.flags & TypeFlags.Union) && getConstituentCount(source) * getConstituentCount(target) < 4) {
if (source.flags & TypeFlags.UnionOrIntersection || target.flags & TypeFlags.UnionOrIntersection) {
// We skip caching when source or target is a union with no more than three constituents.
result = structuredTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck);
}
else if (source.flags & TypeFlags.UnionOrIntersection || target.flags & TypeFlags.UnionOrIntersection) {
result = recursiveTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck, recursionFlags);
}
if (!result && !(source.flags & TypeFlags.Union) && (source.flags & (TypeFlags.StructuredOrInstantiable) || target.flags & TypeFlags.StructuredOrInstantiable)) {
if (result = recursiveTypeRelatedTo(source, target, reportErrors, intersectionState, recursionFlags)) {
resetErrorInfo(saveErrorInfo);
result = (source.flags & TypeFlags.Union || target.flags & TypeFlags.Union) && getConstituentCount(source) * getConstituentCount(target) < 4 ?
structuredTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck) :
recursiveTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck, recursionFlags);
// The ordered decomposition above doesn't handle all cases. Specifically, we also need to handle:
// (1) Source is an intersection of object types { a } & { b } and target is an object type { a, b }.
// (2) Source is an object type { a, b: boolean } and target is a union { a, b: true } | { a, b: false }.
// (3) Source is an intersection { a } & { b: boolean } and target is a union { a, b: true } | { a, b: false }.
// (4) Source is an instantiable type with a union constraint and target is a union.
if (!result && (source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Object ||
(source.flags & (TypeFlags.Object | TypeFlags.Intersection) && isUnionContainingMultipleObjectLikeTypes(target)) ||
(target.flags & TypeFlags.Union && hasStructuredOrInstantiableConstraint(source)))) {
if (result = recursiveTypeRelatedTo(source, target, reportErrors, intersectionState, recursionFlags)) {
resetErrorInfo(saveErrorInfo);
}
}
}
else if (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable) {
result = recursiveTypeRelatedTo(source, target, reportErrors, intersectionState, recursionFlags);
}
if (!result && source.flags & (TypeFlags.Intersection | TypeFlags.TypeParameter)) {
// The combined constraint of an intersection type is the intersection of the constraints of
// the constituents. When an intersection type contains instantiable types with union type
Expand Down Expand Up @@ -18628,8 +18647,11 @@ namespace ts {
}
}
if (reportErrors) {
// Elaborate only if we can find a best matching type in the target union
const bestMatchingType = getBestMatchingType(source, target, isRelatedTo);
isRelatedTo(source, bestMatchingType || targetTypes[targetTypes.length - 1], RecursionFlags.Target, /*reportErrors*/ true);
if (bestMatchingType) {
isRelatedTo(source, bestMatchingType, RecursionFlags.Target, /*reportErrors*/ true);
}
}
return Ternary.False;
}
Expand Down Expand Up @@ -35937,34 +35959,26 @@ namespace ts {
return;
}

let headMessage: DiagnosticMessage;
let expectedReturnType: Type;
const headMessage = getDiagnosticHeadMessageForDecoratorResolution(node);
let errorInfo: DiagnosticMessageChain | undefined;
switch (node.parent.kind) {
case SyntaxKind.ClassDeclaration:
headMessage = Diagnostics.Decorator_function_return_type_0_is_not_assignable_to_type_1;
const classSymbol = getSymbolOfNode(node.parent);
const classConstructorType = getTypeOfSymbol(classSymbol);
expectedReturnType = getUnionType([classConstructorType, voidType]);
break;

case SyntaxKind.Parameter:
expectedReturnType = voidType;
errorInfo = chainDiagnosticMessages(
/*details*/ undefined,
Diagnostics.The_return_type_of_a_parameter_decorator_function_must_be_either_void_or_any);

break;

case SyntaxKind.PropertyDeclaration:
case SyntaxKind.Parameter:
headMessage = Diagnostics.Decorator_function_return_type_is_0_but_is_expected_to_be_void_or_any;
expectedReturnType = voidType;
errorInfo = chainDiagnosticMessages(
/*details*/ undefined,
Diagnostics.The_return_type_of_a_property_decorator_function_must_be_either_void_or_any);
break;

case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
headMessage = Diagnostics.Decorator_function_return_type_0_is_not_assignable_to_type_1;
const methodType = getTypeOfNode(node.parent);
const descriptorType = createTypedPropertyDescriptorType(methodType);
expectedReturnType = getUnionType([descriptorType, voidType]);
Expand All @@ -35978,8 +35992,7 @@ namespace ts {
returnType,
expectedReturnType,
node,
headMessage,
() => errorInfo);
headMessage);
}

/**
Expand Down Expand Up @@ -44217,27 +44230,26 @@ namespace ts {

function findMostOverlappyType(source: Type, unionTarget: UnionOrIntersectionType) {
let bestMatch: Type | undefined;
let matchingCount = 0;
for (const target of unionTarget.types) {
const overlap = getIntersectionType([getIndexType(source), getIndexType(target)]);
if (overlap.flags & TypeFlags.Index) {
// perfect overlap of keys
bestMatch = target;
matchingCount = Infinity;
}
else if (overlap.flags & TypeFlags.Union) {
// We only want to account for literal types otherwise.
// If we have a union of index types, it seems likely that we
// needed to elaborate between two generic mapped types anyway.
const len = length(filter((overlap as UnionType).types, isUnitType));
if (len >= matchingCount) {
bestMatch = target;
matchingCount = len;
}
}
else if (isUnitType(overlap) && 1 >= matchingCount) {
bestMatch = target;
matchingCount = 1;
if (!(source.flags & (TypeFlags.Primitive | TypeFlags.InstantiablePrimitive))) {
let matchingCount = 0;
for (const target of unionTarget.types) {
if (!(target.flags & (TypeFlags.Primitive | TypeFlags.InstantiablePrimitive))) {
const overlap = getIntersectionType([getIndexType(source), getIndexType(target)]);
if (overlap.flags & TypeFlags.Index) {
// perfect overlap of keys
return target;
}
else if (isUnitType(overlap) || overlap.flags & TypeFlags.Union) {
// We only want to account for literal types otherwise.
// If we have a union of index types, it seems likely that we
// needed to elaborate between two generic mapped types anyway.
const len = overlap.flags & TypeFlags.Union ? countWhere((overlap as UnionType).types, isUnitType) : 1;
if (len >= matchingCount) {
bestMatch = target;
matchingCount = len;
}
}
}
}
}
return bestMatch;
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,14 @@
"category": "Error",
"code": 1268
},
"Decorator function return type '{0}' is not assignable to type '{1}'.": {
"category": "Error",
"code": 1269
},
"Decorator function return type is '{0}' but is expected to be 'void' or 'any'.": {
"category": "Error",
"code": 1270
},

"'with' statements are not allowed in an async function block.": {
"category": "Error",
Expand Down

0 comments on commit 628da10

Please sign in to comment.