Skip to content

Commit 09c7d02

Browse files
committed
Do less work in addtional check of intersections
1 parent 2c938a4 commit 09c7d02

File tree

1 file changed

+20
-14
lines changed

1 file changed

+20
-14
lines changed

src/compiler/checker.ts

+20-14
Original file line numberDiff line numberDiff line change
@@ -20937,26 +20937,32 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2093720937
result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState);
2093820938
}
2093920939
}
20940-
// For certain combinations involving intersections and optional, excess, or mismatched properties we need
20941-
// an extra property check where the intersection is viewed as a single object. The following are motivating
20942-
// examples that all should be errors, but aren't without this extra property check:
20940+
// When the target is an intersection we need an extra property check in order to detect nested excess
20941+
// properties and nested weak types. The following are motivating examples that all should be errors, but
20942+
// aren't without this extra property check:
2094320943
//
2094420944
// let obj: { a: { x: string } } & { c: number } = { a: { x: 'hello', y: 2 }, c: 5 }; // Nested excess property
2094520945
//
2094620946
// declare let wrong: { a: { y: string } };
2094720947
// let weak: { a?: { x?: number } } & { c?: string } = wrong; // Nested weak object type
2094820948
//
20949-
// function foo<T extends object>(x: { a?: string }, y: T & { a: boolean }) {
20950-
// x = y; // Mismatched property in source intersection
20951-
// }
20952-
if (result && !(intersectionState & IntersectionState.Target) && (
20953-
target.flags & TypeFlags.Intersection && !isGenericObjectType(target) && source.flags & (TypeFlags.Object | TypeFlags.Intersection) ||
20954-
isNonGenericObjectType(target) && !isArrayOrTupleType(target) && source.flags & TypeFlags.Intersection && getApparentType(source).flags & TypeFlags.StructuredType && !some((source as IntersectionType).types, t => !!(getObjectFlags(t) & ObjectFlags.NonInferrableType)))) {
20955-
result &= propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, IntersectionState.None);
20949+
if (result && !(intersectionState & IntersectionState.Target) &&
20950+
target.flags & TypeFlags.Intersection && !isGenericObjectType(target) && source.flags & (TypeFlags.Object | TypeFlags.Intersection)) {
20951+
result &= propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, /*optionalsOnly*/ false, IntersectionState.None);
2095620952
if (result && isObjectLiteralType(source) && getObjectFlags(source) & ObjectFlags.FreshLiteral) {
2095720953
result &= indexSignaturesRelatedTo(source, target, /*sourceIsPrimitive*/ false, reportErrors, IntersectionState.None);
2095820954
}
2095920955
}
20956+
// When the source is an intersection we need an extra check of any optional properties in the target to
20957+
// detect possible mismatched property types. For example:
20958+
//
20959+
// function foo<T extends object>(x: { a?: string }, y: T & { a: boolean }) {
20960+
// x = y; // Mismatched property in source intersection
20961+
// }
20962+
//
20963+
else if (result && isNonGenericObjectType(target) && !isArrayOrTupleType(target) && some(getPropertiesOfType(target), p => !!(p.flags & SymbolFlags.Optional)) && source.flags & TypeFlags.Intersection && getApparentType(source).flags & TypeFlags.StructuredType && !some((source as IntersectionType).types, t => !!(getObjectFlags(t) & ObjectFlags.NonInferrableType))) {
20964+
result &= propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, /*optionalsOnly*/ true, intersectionState);
20965+
}
2096020966
}
2096120967
if (result) {
2096220968
resetErrorInfo(saveErrorInfo);
@@ -21463,7 +21469,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2146321469
if (sourceFlags & (TypeFlags.Object | TypeFlags.Intersection) && targetFlags & TypeFlags.Object) {
2146421470
// Report structural errors only if we haven't reported any errors yet
2146521471
const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo.errorInfo && !sourceIsPrimitive;
21466-
result = propertiesRelatedTo(source, target, reportStructuralErrors, /*excludedProperties*/ undefined, intersectionState);
21472+
result = propertiesRelatedTo(source, target, reportStructuralErrors, /*excludedProperties*/ undefined, /*optionalsOnly*/ false, intersectionState);
2146721473
if (result) {
2146821474
result &= signaturesRelatedTo(source, target, SignatureKind.Call, reportStructuralErrors, intersectionState);
2146921475
if (result) {
@@ -21638,7 +21644,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2163821644
// Compare the remaining non-discriminant properties of each match.
2163921645
let result = Ternary.True;
2164021646
for (const type of matchingTypes) {
21641-
result &= propertiesRelatedTo(source, type, /*reportErrors*/ false, excludedProperties, IntersectionState.None);
21647+
result &= propertiesRelatedTo(source, type, /*reportErrors*/ false, excludedProperties, /*optionalsOnly*/ false, IntersectionState.None);
2164221648
if (result) {
2164321649
result &= signaturesRelatedTo(source, type, SignatureKind.Call, /*reportStructuralErrors*/ false, IntersectionState.None);
2164421650
if (result) {
@@ -21805,7 +21811,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2180521811
// No array like or unmatched property error - just issue top level error (errorInfo = undefined)
2180621812
}
2180721813

21808-
function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean, excludedProperties: Set<__String> | undefined, intersectionState: IntersectionState): Ternary {
21814+
function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean, excludedProperties: Set<__String> | undefined, optionalsOnly: boolean, intersectionState: IntersectionState): Ternary {
2180921815
if (relation === identityRelation) {
2181021816
return propertiesIdenticalTo(source, target, excludedProperties);
2181121817
}
@@ -21940,7 +21946,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2194021946
const numericNamesOnly = isTupleType(source) && isTupleType(target);
2194121947
for (const targetProp of excludeProperties(properties, excludedProperties)) {
2194221948
const name = targetProp.escapedName;
21943-
if (!(targetProp.flags & SymbolFlags.Prototype) && (!numericNamesOnly || isNumericLiteralName(name) || name === "length")) {
21949+
if (!(targetProp.flags & SymbolFlags.Prototype) && (!numericNamesOnly || isNumericLiteralName(name) || name === "length") && (!optionalsOnly || targetProp.flags & SymbolFlags.Optional)) {
2194421950
const sourceProp = getPropertyOfType(source, name);
2194521951
if (sourceProp && sourceProp !== targetProp) {
2194621952
const related = propertyRelatedTo(source, target, sourceProp, targetProp, getNonMissingTypeOfSymbol, reportErrors, intersectionState, relation === comparableRelation);

0 commit comments

Comments
 (0)