@@ -634,6 +634,7 @@ namespace ts {
634
634
isArrayLikeType,
635
635
isTypeInvalidDueToUnionDiscriminant,
636
636
getExactOptionalUnassignableProperties,
637
+ isExactOptionalPropertyMismatch,
637
638
getAllPossiblePropertiesOfTypes,
638
639
getSuggestedSymbolForNonexistentProperty,
639
640
getSuggestionForNonexistentProperty,
@@ -16754,24 +16755,29 @@ namespace ts {
16754
16755
let sourcePropType = getIndexedAccessTypeOrUndefined(source, nameType);
16755
16756
if (!sourcePropType) continue;
16756
16757
const propName = getPropertyNameFromIndex(nameType, /*accessNode*/ undefined);
16757
- const targetIsOptional = !!(propName && (getPropertyOfType(target, propName) || unknownSymbol).flags & SymbolFlags.Optional);
16758
- const sourceIsOptional = !!(propName && (getPropertyOfType(source, propName) || unknownSymbol).flags & SymbolFlags.Optional);
16759
- targetPropType = removeMissingType(targetPropType, targetIsOptional);
16760
- sourcePropType = removeMissingType(sourcePropType, targetIsOptional && sourceIsOptional);
16761
16758
if (!checkTypeRelatedTo(sourcePropType, targetPropType, relation, /*errorNode*/ undefined)) {
16762
16759
const elaborated = next && elaborateError(next, sourcePropType, targetPropType, relation, /*headMessage*/ undefined, containingMessageChain, errorOutputContainer);
16763
- if (elaborated) {
16764
- reportedError = true;
16765
- }
16766
- else {
16760
+ reportedError = true;
16761
+ if (!elaborated) {
16767
16762
// Issue error on the prop itself, since the prop couldn't elaborate the error
16768
16763
const resultObj: { errors?: Diagnostic[] } = errorOutputContainer || {};
16769
16764
// Use the expression type, if available
16770
16765
const specificSource = next ? checkExpressionForMutableLocationWithContextualType(next, sourcePropType) : sourcePropType;
16771
- const result = checkTypeRelatedTo(specificSource, targetPropType, relation, prop, errorMessage, containingMessageChain, resultObj);
16772
- if (result && specificSource !== sourcePropType) {
16773
- // If for whatever reason the expression type doesn't yield an error, make sure we still issue an error on the sourcePropType
16774
- checkTypeRelatedTo(sourcePropType, targetPropType, relation, prop, errorMessage, containingMessageChain, resultObj);
16766
+ if (exactOptionalPropertyTypes && isExactOptionalPropertyMismatch(specificSource, targetPropType)) {
16767
+ const diag = createDiagnosticForNode(prop, Diagnostics.Type_0_is_not_assignable_to_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_type_of_the_target, typeToString(specificSource), typeToString(targetPropType));
16768
+ diagnostics.add(diag);
16769
+ resultObj.errors = [diag];
16770
+ }
16771
+ else {
16772
+ const targetIsOptional = !!(propName && (getPropertyOfType(target, propName) || unknownSymbol).flags & SymbolFlags.Optional);
16773
+ const sourceIsOptional = !!(propName && (getPropertyOfType(source, propName) || unknownSymbol).flags & SymbolFlags.Optional);
16774
+ targetPropType = removeMissingType(targetPropType, targetIsOptional);
16775
+ sourcePropType = removeMissingType(sourcePropType, targetIsOptional && sourceIsOptional);
16776
+ const result = checkTypeRelatedTo(specificSource, targetPropType, relation, prop, errorMessage, containingMessageChain, resultObj);
16777
+ if (result && specificSource !== sourcePropType) {
16778
+ // If for whatever reason the expression type doesn't yield an error, make sure we still issue an error on the sourcePropType
16779
+ checkTypeRelatedTo(sourcePropType, targetPropType, relation, prop, errorMessage, containingMessageChain, resultObj);
16780
+ }
16775
16781
}
16776
16782
if (resultObj.errors) {
16777
16783
const reportedDiag = resultObj.errors[resultObj.errors.length - 1];
@@ -16799,7 +16805,6 @@ namespace ts {
16799
16805
}
16800
16806
}
16801
16807
}
16802
- reportedError = true;
16803
16808
}
16804
16809
}
16805
16810
}
@@ -19593,13 +19598,14 @@ namespace ts {
19593
19598
}
19594
19599
19595
19600
function getExactOptionalUnassignableProperties(source: Type, target: Type) {
19596
- return checker.getPropertiesOfType(target).filter(targetProp => {
19597
- const sourceProp = getPropertyOfType(source, targetProp.escapedName);
19598
- return sourceProp && targetProp.valueDeclaration
19599
- && maybeTypeOfKind(getTypeOfSymbol(sourceProp), TypeFlags.Undefined)
19600
- && hasQuestionToken(targetProp.valueDeclaration)
19601
- && containsMissingType(getTypeOfSymbol(targetProp));
19602
- });
19601
+ return getPropertiesOfType(target)
19602
+ .filter(targetProp => isExactOptionalPropertyMismatch(getTypeOfPropertyOfType(source, targetProp.escapedName), getTypeOfSymbol(targetProp)));
19603
+ }
19604
+
19605
+ function isExactOptionalPropertyMismatch(source: Type | undefined, target: Type | undefined) {
19606
+ return !!source && !!target
19607
+ && !(getObjectFlags(source) & ObjectFlags.Tuple) && !(getObjectFlags(target) & ObjectFlags.Tuple)
19608
+ && maybeTypeOfKind(source, TypeFlags.Undefined) && !!containsMissingType(target);
19603
19609
}
19604
19610
19605
19611
function getBestMatchingType(source: Type, target: UnionOrIntersectionType, isRelatedTo = compareTypesAssignable) {
@@ -32463,8 +32469,16 @@ namespace ts {
32463
32469
Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access,
32464
32470
Diagnostics.The_left_hand_side_of_an_assignment_expression_may_not_be_an_optional_property_access)
32465
32471
&& (!isIdentifier(left) || unescapeLeadingUnderscores(left.escapedText) !== "exports")) {
32472
+
32473
+ let headMessage: DiagnosticMessage | undefined;
32474
+ if (exactOptionalPropertyTypes && isPropertyAccessExpression(left) && maybeTypeOfKind(valueType, TypeFlags.Undefined)) {
32475
+ const target = getTypeOfPropertyOfType(getTypeOfExpression(left.expression), left.name.escapedText);
32476
+ if (isExactOptionalPropertyMismatch(valueType, target)) {
32477
+ headMessage = Diagnostics.Type_0_is_not_assignable_to_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_type_of_the_target;
32478
+ }
32479
+ }
32466
32480
// to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported
32467
- checkTypeAssignableToAndOptionallyElaborate(valueType, leftType, left, right);
32481
+ checkTypeAssignableToAndOptionallyElaborate(valueType, leftType, left, right, headMessage );
32468
32482
}
32469
32483
}
32470
32484
}
0 commit comments