@@ -7473,6 +7473,13 @@ namespace ts {
7473
7473
return type.resolvedProperties;
7474
7474
}
7475
7475
7476
+ function getPossiblePropertiesOfUnionType(type: UnionType): Symbol[] {
7477
+ // The following is for effects - getUnionOrIntersectionProperty will cache all the possible union properties into `type`
7478
+ void map(flatMap(type.types, getPropertiesOfType), p => getUnionOrIntersectionProperty(type, p.escapedName));
7479
+ // And we can then (uniquely) fetch them out of the cache, instead of as a result of the above call.
7480
+ return !type.propertyCache ? emptyArray : arrayFrom(type.propertyCache.values());
7481
+ }
7482
+
7476
7483
function getPropertiesOfType(type: Type): Symbol[] {
7477
7484
type = getApparentType(type);
7478
7485
return type.flags & TypeFlags.UnionOrIntersection ?
@@ -7832,7 +7839,7 @@ namespace ts {
7832
7839
const isUnion = containingType.flags & TypeFlags.Union;
7833
7840
const excludeModifiers = isUnion ? ModifierFlags.NonPublicAccessibilityModifier : 0;
7834
7841
// Flags we want to propagate to the result if they exist in all source symbols
7835
- let commonFlags = isUnion ? SymbolFlags.None : SymbolFlags.Optional;
7842
+ let optionalFlag = isUnion ? SymbolFlags.None : SymbolFlags.Optional;
7836
7843
let syntheticFlag = CheckFlags.SyntheticMethod;
7837
7844
let checkFlags = 0;
7838
7845
for (const current of containingType.types) {
@@ -7841,7 +7848,12 @@ namespace ts {
7841
7848
const prop = getPropertyOfType(type, name);
7842
7849
const modifiers = prop ? getDeclarationModifierFlagsFromSymbol(prop) : 0;
7843
7850
if (prop && !(modifiers & excludeModifiers)) {
7844
- commonFlags &= prop.flags;
7851
+ if (isUnion) {
7852
+ optionalFlag |= (prop.flags & SymbolFlags.Optional);
7853
+ }
7854
+ else {
7855
+ optionalFlag &= prop.flags;
7856
+ }
7845
7857
const id = "" + getSymbolId(prop);
7846
7858
if (!propSet.has(id)) {
7847
7859
propSet.set(id, prop);
@@ -7859,10 +7871,11 @@ namespace ts {
7859
7871
const indexInfo = !isLateBoundName(name) && (isNumericLiteralName(name) && getIndexInfoOfType(type, IndexKind.Number) || getIndexInfoOfType(type, IndexKind.String));
7860
7872
if (indexInfo) {
7861
7873
checkFlags |= indexInfo.isReadonly ? CheckFlags.Readonly : 0;
7874
+ checkFlags |= CheckFlags.WritePartial;
7862
7875
indexTypes = append(indexTypes, isTupleType(type) ? getRestTypeOfTupleType(type) || undefinedType : indexInfo.type);
7863
7876
}
7864
7877
else {
7865
- checkFlags |= CheckFlags.Partial ;
7878
+ checkFlags |= CheckFlags.ReadPartial ;
7866
7879
}
7867
7880
}
7868
7881
}
@@ -7871,7 +7884,7 @@ namespace ts {
7871
7884
return undefined;
7872
7885
}
7873
7886
const props = arrayFrom(propSet.values());
7874
- if (props.length === 1 && !(checkFlags & CheckFlags.Partial ) && !indexTypes) {
7887
+ if (props.length === 1 && !(checkFlags & CheckFlags.ReadPartial ) && !indexTypes) {
7875
7888
return props[0];
7876
7889
}
7877
7890
let declarations: Declaration[] | undefined;
@@ -7902,7 +7915,7 @@ namespace ts {
7902
7915
propTypes.push(type);
7903
7916
}
7904
7917
addRange(propTypes, indexTypes);
7905
- const result = createSymbol(SymbolFlags.Property | commonFlags , name, syntheticFlag | checkFlags);
7918
+ const result = createSymbol(SymbolFlags.Property | optionalFlag , name, syntheticFlag | checkFlags);
7906
7919
result.containingType = containingType;
7907
7920
if (!hasNonUniformValueDeclaration && firstValueDeclaration) {
7908
7921
result.valueDeclaration = firstValueDeclaration;
@@ -7939,7 +7952,7 @@ namespace ts {
7939
7952
function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: __String): Symbol | undefined {
7940
7953
const property = getUnionOrIntersectionProperty(type, name);
7941
7954
// We need to filter out partial properties in union types
7942
- return property && !(getCheckFlags(property) & CheckFlags.Partial ) ? property : undefined;
7955
+ return property && !(getCheckFlags(property) & CheckFlags.ReadPartial ) ? property : undefined;
7943
7956
}
7944
7957
7945
7958
/**
@@ -12204,25 +12217,6 @@ namespace ts {
12204
12217
}
12205
12218
}
12206
12219
12207
- function isUnionOrIntersectionTypeWithoutNullableConstituents(type: Type): boolean {
12208
- if (!(type.flags & TypeFlags.UnionOrIntersection)) {
12209
- return false;
12210
- }
12211
- // at this point we know that this is union or intersection type possibly with nullable constituents.
12212
- // check if we still will have compound type if we ignore nullable components.
12213
- let seenNonNullable = false;
12214
- for (const t of (<UnionOrIntersectionType>type).types) {
12215
- if (t.flags & TypeFlags.Nullable) {
12216
- continue;
12217
- }
12218
- if (seenNonNullable) {
12219
- return true;
12220
- }
12221
- seenNonNullable = true;
12222
- }
12223
- return false;
12224
- }
12225
-
12226
12220
/**
12227
12221
* Compare two types and return
12228
12222
* * Ternary.True if they are related with no assumptions,
@@ -12277,21 +12271,15 @@ namespace ts {
12277
12271
isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True;
12278
12272
12279
12273
const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes);
12280
- if (isObjectLiteralType(source) && getObjectFlags(source) & ObjectFlags.FreshLiteral) {
12274
+ const isPerformingExcessPropertyChecks = (isObjectLiteralType(source) && getObjectFlags(source) & ObjectFlags.FreshLiteral);
12275
+ if (isPerformingExcessPropertyChecks) {
12281
12276
const discriminantType = target.flags & TypeFlags.Union ? findMatchingDiscriminantType(source, target as UnionType) : undefined;
12282
12277
if (hasExcessProperties(<FreshObjectLiteralType>source, target, discriminantType, reportErrors)) {
12283
12278
if (reportErrors) {
12284
12279
reportRelationError(headMessage, source, target);
12285
12280
}
12286
12281
return Ternary.False;
12287
12282
}
12288
- // Above we check for excess properties with respect to the entire target type. When union
12289
- // and intersection types are further deconstructed on the target side, we don't want to
12290
- // make the check again (as it might fail for a partial target type). Therefore we obtain
12291
- // the regular source type and proceed with that.
12292
- if (isUnionOrIntersectionTypeWithoutNullableConstituents(target) && !discriminantType) {
12293
- source = getRegularTypeOfObjectLiteral(source);
12294
- }
12295
12283
}
12296
12284
12297
12285
if (relation !== comparableRelation && !isApparentIntersectionConstituent &&
@@ -12327,11 +12315,24 @@ namespace ts {
12327
12315
}
12328
12316
else {
12329
12317
if (target.flags & TypeFlags.Union) {
12330
- result = typeRelatedToSomeType(source, <UnionType>target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive));
12318
+ result = typeRelatedToSomeType(getRegularTypeOfObjectLiteral(source), <UnionType>target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive));
12319
+ if (result && isPerformingExcessPropertyChecks) {
12320
+ // Validate against excess props using the original `source`
12321
+ const discriminantType = target.flags & TypeFlags.Union ? findMatchingDiscriminantType(source, target as UnionType) : undefined;
12322
+ if (!propertiesRelatedTo(source, discriminantType || target, reportErrors)) {
12323
+ return Ternary.False;
12324
+ }
12325
+ }
12331
12326
}
12332
12327
else if (target.flags & TypeFlags.Intersection) {
12333
12328
isIntersectionConstituent = true; // set here to affect the following trio of checks
12334
- result = typeRelatedToEachType(source, target as IntersectionType, reportErrors);
12329
+ result = typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors);
12330
+ if (result && isPerformingExcessPropertyChecks) {
12331
+ // Validate against excess props using the original `source`
12332
+ if (!propertiesRelatedTo(source, target, reportErrors)) {
12333
+ return Ternary.False;
12334
+ }
12335
+ }
12335
12336
}
12336
12337
else if (source.flags & TypeFlags.Intersection) {
12337
12338
// Check to see if any constituents of the intersection are immediately related to the target.
@@ -12431,7 +12432,7 @@ namespace ts {
12431
12432
// check excess properties against discriminant type only, not the entire union
12432
12433
return hasExcessProperties(source, discriminant, /*discriminant*/ undefined, reportErrors);
12433
12434
}
12434
- for (const prop of getPropertiesOfObjectType (source)) {
12435
+ for (const prop of getPropertiesOfType (source)) {
12435
12436
if (shouldCheckAsExcessProperty(prop, source.symbol) && !isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) {
12436
12437
if (reportErrors) {
12437
12438
// Report error in terms of object types in the target as those are the only ones
@@ -13150,7 +13151,9 @@ namespace ts {
13150
13151
}
13151
13152
}
13152
13153
}
13153
- const properties = getPropertiesOfObjectType(target);
13154
+ // We only call this for union target types when we're attempting to do excess property checking - in those cases, we want to get _all possible props_
13155
+ // from the target union, across all members
13156
+ const properties = target.flags & TypeFlags.Union ? getPossiblePropertiesOfUnionType(target as UnionType) : getPropertiesOfType(target);
13154
13157
for (const targetProp of properties) {
13155
13158
if (!(targetProp.flags & SymbolFlags.Prototype)) {
13156
13159
const sourceProp = getPropertyOfType(source, targetProp.escapedName);
@@ -14550,9 +14553,9 @@ namespace ts {
14550
14553
}
14551
14554
14552
14555
function* getUnmatchedProperties(source: Type, target: Type, requireOptionalProperties: boolean, matchDiscriminantProperties: boolean) {
14553
- const properties = target.flags & TypeFlags.Intersection ? getPropertiesOfUnionOrIntersectionType(<IntersectionType> target) : getPropertiesOfObjectType (target);
14556
+ const properties = target.flags & TypeFlags.Union ? getPossiblePropertiesOfUnionType( target as UnionType ) : getPropertiesOfType (target);
14554
14557
for (const targetProp of properties) {
14555
- if (requireOptionalProperties || !(targetProp.flags & SymbolFlags.Optional)) {
14558
+ if (requireOptionalProperties || !(targetProp.flags & SymbolFlags.Optional || getCheckFlags(targetProp) & CheckFlags.Partial )) {
14556
14559
const sourceProp = getPropertyOfType(source, targetProp.escapedName);
14557
14560
if (!sourceProp) {
14558
14561
yield targetProp;
0 commit comments