@@ -114,6 +114,11 @@ namespace ts {
114
114
getIdentifierCount: () => sum(host.getSourceFiles(), "identifierCount"),
115
115
getSymbolCount: () => sum(host.getSourceFiles(), "symbolCount") + symbolCount,
116
116
getTypeCount: () => typeCount,
117
+ getRelationCacheSizes: () => ({
118
+ assignable: assignableRelation.size,
119
+ identity: identityRelation.size,
120
+ subtype: subtypeRelation.size,
121
+ }),
117
122
isUndefinedSymbol: symbol => symbol === undefinedSymbol,
118
123
isArgumentsSymbol: symbol => symbol === argumentsSymbol,
119
124
isUnknownSymbol: symbol => symbol === unknownSymbol,
@@ -7474,6 +7479,25 @@ namespace ts {
7474
7479
return type.resolvedProperties;
7475
7480
}
7476
7481
7482
+ function getPossiblePropertiesOfUnionType(type: UnionType): Symbol[] {
7483
+ if (type.possiblePropertyCache) {
7484
+ return type.possiblePropertyCache.size ? arrayFrom(type.possiblePropertyCache.values()) : emptyArray;
7485
+ }
7486
+ type.possiblePropertyCache = createSymbolTable();
7487
+ for (const t of type.types) {
7488
+ for (const p of getPropertiesOfType(t)) {
7489
+ if (!type.possiblePropertyCache.has(p.escapedName)) {
7490
+ const prop = getUnionOrIntersectionProperty(type, p.escapedName);
7491
+ if (prop) {
7492
+ type.possiblePropertyCache.set(p.escapedName, prop);
7493
+ }
7494
+ }
7495
+ }
7496
+ }
7497
+ // We can't simply use the normal property cache here, since that will contain cached apparent type members :(
7498
+ return type.possiblePropertyCache.size ? arrayFrom(type.possiblePropertyCache.values()) : emptyArray;
7499
+ }
7500
+
7477
7501
function getPropertiesOfType(type: Type): Symbol[] {
7478
7502
type = getApparentType(type);
7479
7503
return type.flags & TypeFlags.UnionOrIntersection ?
@@ -7831,7 +7855,7 @@ namespace ts {
7831
7855
const isUnion = containingType.flags & TypeFlags.Union;
7832
7856
const excludeModifiers = isUnion ? ModifierFlags.NonPublicAccessibilityModifier : 0;
7833
7857
// Flags we want to propagate to the result if they exist in all source symbols
7834
- let commonFlags = isUnion ? SymbolFlags.None : SymbolFlags.Optional;
7858
+ let optionalFlag = isUnion ? SymbolFlags.None : SymbolFlags.Optional;
7835
7859
let syntheticFlag = CheckFlags.SyntheticMethod;
7836
7860
let checkFlags = 0;
7837
7861
for (const current of containingType.types) {
@@ -7840,7 +7864,12 @@ namespace ts {
7840
7864
const prop = getPropertyOfType(type, name);
7841
7865
const modifiers = prop ? getDeclarationModifierFlagsFromSymbol(prop) : 0;
7842
7866
if (prop && !(modifiers & excludeModifiers)) {
7843
- commonFlags &= prop.flags;
7867
+ if (isUnion) {
7868
+ optionalFlag |= (prop.flags & SymbolFlags.Optional);
7869
+ }
7870
+ else {
7871
+ optionalFlag &= prop.flags;
7872
+ }
7844
7873
const id = "" + getSymbolId(prop);
7845
7874
if (!propSet.has(id)) {
7846
7875
propSet.set(id, prop);
@@ -7858,10 +7887,11 @@ namespace ts {
7858
7887
const indexInfo = !isLateBoundName(name) && (isNumericLiteralName(name) && getIndexInfoOfType(type, IndexKind.Number) || getIndexInfoOfType(type, IndexKind.String));
7859
7888
if (indexInfo) {
7860
7889
checkFlags |= indexInfo.isReadonly ? CheckFlags.Readonly : 0;
7890
+ checkFlags |= CheckFlags.WritePartial;
7861
7891
indexTypes = append(indexTypes, isTupleType(type) ? getRestTypeOfTupleType(type) || undefinedType : indexInfo.type);
7862
7892
}
7863
7893
else {
7864
- checkFlags |= CheckFlags.Partial ;
7894
+ checkFlags |= CheckFlags.ReadPartial ;
7865
7895
}
7866
7896
}
7867
7897
}
@@ -7870,7 +7900,7 @@ namespace ts {
7870
7900
return undefined;
7871
7901
}
7872
7902
const props = arrayFrom(propSet.values());
7873
- if (props.length === 1 && !(checkFlags & CheckFlags.Partial ) && !indexTypes) {
7903
+ if (props.length === 1 && !(checkFlags & CheckFlags.ReadPartial ) && !indexTypes) {
7874
7904
return props[0];
7875
7905
}
7876
7906
let declarations: Declaration[] | undefined;
@@ -7901,7 +7931,7 @@ namespace ts {
7901
7931
propTypes.push(type);
7902
7932
}
7903
7933
addRange(propTypes, indexTypes);
7904
- const result = createSymbol(SymbolFlags.Property | commonFlags , name, syntheticFlag | checkFlags);
7934
+ const result = createSymbol(SymbolFlags.Property | optionalFlag , name, syntheticFlag | checkFlags);
7905
7935
result.containingType = containingType;
7906
7936
if (!hasNonUniformValueDeclaration && firstValueDeclaration) {
7907
7937
result.valueDeclaration = firstValueDeclaration;
@@ -7938,7 +7968,7 @@ namespace ts {
7938
7968
function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: __String): Symbol | undefined {
7939
7969
const property = getUnionOrIntersectionProperty(type, name);
7940
7970
// We need to filter out partial properties in union types
7941
- return property && !(getCheckFlags(property) & CheckFlags.Partial ) ? property : undefined;
7971
+ return property && !(getCheckFlags(property) & CheckFlags.ReadPartial ) ? property : undefined;
7942
7972
}
7943
7973
7944
7974
/**
@@ -12277,25 +12307,6 @@ namespace ts {
12277
12307
return true;
12278
12308
}
12279
12309
12280
- function isUnionOrIntersectionTypeWithoutNullableConstituents(type: Type): boolean {
12281
- if (!(type.flags & TypeFlags.UnionOrIntersection)) {
12282
- return false;
12283
- }
12284
- // at this point we know that this is union or intersection type possibly with nullable constituents.
12285
- // check if we still will have compound type if we ignore nullable components.
12286
- let seenNonNullable = false;
12287
- for (const t of (<UnionOrIntersectionType>type).types) {
12288
- if (t.flags & TypeFlags.Nullable) {
12289
- continue;
12290
- }
12291
- if (seenNonNullable) {
12292
- return true;
12293
- }
12294
- seenNonNullable = true;
12295
- }
12296
- return false;
12297
- }
12298
-
12299
12310
/**
12300
12311
* Compare two types and return
12301
12312
* * Ternary.True if they are related with no assumptions,
@@ -12350,21 +12361,15 @@ namespace ts {
12350
12361
isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True;
12351
12362
12352
12363
const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes);
12353
- if (isObjectLiteralType(source) && getObjectFlags(source) & ObjectFlags.FreshLiteral) {
12364
+ const isPerformingExcessPropertyChecks = (isObjectLiteralType(source) && getObjectFlags(source) & ObjectFlags.FreshLiteral);
12365
+ if (isPerformingExcessPropertyChecks) {
12354
12366
const discriminantType = target.flags & TypeFlags.Union ? findMatchingDiscriminantType(source, target as UnionType) : undefined;
12355
12367
if (hasExcessProperties(<FreshObjectLiteralType>source, target, discriminantType, reportErrors)) {
12356
12368
if (reportErrors) {
12357
12369
reportRelationError(headMessage, source, target);
12358
12370
}
12359
12371
return Ternary.False;
12360
12372
}
12361
- // Above we check for excess properties with respect to the entire target type. When union
12362
- // and intersection types are further deconstructed on the target side, we don't want to
12363
- // make the check again (as it might fail for a partial target type). Therefore we obtain
12364
- // the regular source type and proceed with that.
12365
- if (isUnionOrIntersectionTypeWithoutNullableConstituents(target) && !discriminantType) {
12366
- source = getRegularTypeOfObjectLiteral(source);
12367
- }
12368
12373
}
12369
12374
12370
12375
if (relation !== comparableRelation && !isApparentIntersectionConstituent &&
@@ -12400,11 +12405,24 @@ namespace ts {
12400
12405
}
12401
12406
else {
12402
12407
if (target.flags & TypeFlags.Union) {
12403
- result = typeRelatedToSomeType(source, <UnionType>target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive));
12408
+ result = typeRelatedToSomeType(getRegularTypeOfObjectLiteral(source), <UnionType>target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive));
12409
+ if (result && isPerformingExcessPropertyChecks) {
12410
+ // Validate against excess props using the original `source`
12411
+ const discriminantType = target.flags & TypeFlags.Union ? findMatchingDiscriminantType(source, target as UnionType) : undefined;
12412
+ if (!propertiesRelatedTo(source, discriminantType || target, reportErrors)) {
12413
+ return Ternary.False;
12414
+ }
12415
+ }
12404
12416
}
12405
12417
else if (target.flags & TypeFlags.Intersection) {
12406
12418
isIntersectionConstituent = true; // set here to affect the following trio of checks
12407
- result = typeRelatedToEachType(source, target as IntersectionType, reportErrors);
12419
+ result = typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors);
12420
+ if (result && isPerformingExcessPropertyChecks) {
12421
+ // Validate against excess props using the original `source`
12422
+ if (!propertiesRelatedTo(source, target, reportErrors)) {
12423
+ return Ternary.False;
12424
+ }
12425
+ }
12408
12426
}
12409
12427
else if (source.flags & TypeFlags.Intersection) {
12410
12428
// Check to see if any constituents of the intersection are immediately related to the target.
@@ -12507,7 +12525,7 @@ namespace ts {
12507
12525
// check excess properties against discriminant type only, not the entire union
12508
12526
return hasExcessProperties(source, discriminant, /*discriminant*/ undefined, reportErrors);
12509
12527
}
12510
- for (const prop of getPropertiesOfObjectType (source)) {
12528
+ for (const prop of getPropertiesOfType (source)) {
12511
12529
if (shouldCheckAsExcessProperty(prop, source.symbol) && !isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) {
12512
12530
if (reportErrors) {
12513
12531
// Report error in terms of object types in the target as those are the only ones
@@ -13234,7 +13252,9 @@ namespace ts {
13234
13252
}
13235
13253
}
13236
13254
}
13237
- const properties = getPropertiesOfObjectType(target);
13255
+ // 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_
13256
+ // from the target union, across all members
13257
+ const properties = target.flags & TypeFlags.Union ? getPossiblePropertiesOfUnionType(target as UnionType) : getPropertiesOfType(target);
13238
13258
for (const targetProp of properties) {
13239
13259
if (!(targetProp.flags & SymbolFlags.Prototype)) {
13240
13260
const sourceProp = getPropertyOfType(source, targetProp.escapedName);
@@ -13282,7 +13302,8 @@ namespace ts {
13282
13302
}
13283
13303
return Ternary.False;
13284
13304
}
13285
- const related = isRelatedTo(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp), reportErrors);
13305
+ // If the target comes from a partial union prop, allow `undefined` in the target type
13306
+ const related = isRelatedTo(getTypeOfSymbol(sourceProp), addOptionality(getTypeOfSymbol(targetProp), !!(getCheckFlags(targetProp) & CheckFlags.Partial)), reportErrors);
13286
13307
if (!related) {
13287
13308
if (reportErrors) {
13288
13309
reportError(Diagnostics.Types_of_property_0_are_incompatible, symbolToString(targetProp));
@@ -14628,9 +14649,9 @@ namespace ts {
14628
14649
}
14629
14650
14630
14651
function* getUnmatchedProperties(source: Type, target: Type, requireOptionalProperties: boolean, matchDiscriminantProperties: boolean) {
14631
- const properties = target.flags & TypeFlags.Intersection ? getPropertiesOfUnionOrIntersectionType(<IntersectionType> target) : getPropertiesOfObjectType (target);
14652
+ const properties = target.flags & TypeFlags.Union ? getPossiblePropertiesOfUnionType( target as UnionType ) : getPropertiesOfType (target);
14632
14653
for (const targetProp of properties) {
14633
- if (requireOptionalProperties || !(targetProp.flags & SymbolFlags.Optional)) {
14654
+ if (requireOptionalProperties || !(targetProp.flags & SymbolFlags.Optional || getCheckFlags(targetProp) & CheckFlags.Partial )) {
14634
14655
const sourceProp = getPropertyOfType(source, targetProp.escapedName);
14635
14656
if (!sourceProp) {
14636
14657
yield targetProp;
@@ -15001,12 +15022,11 @@ namespace ts {
15001
15022
}
15002
15023
// If no inferences can be made to K's constraint, infer from a union of the property types
15003
15024
// in the source to the template type X.
15004
- const valueTypes = compact([
15005
- getIndexTypeOfType(source, IndexKind.String),
15006
- getIndexTypeOfType(source, IndexKind.Number),
15007
- ...map(getPropertiesOfType(source), getTypeOfSymbol)
15008
- ]);
15009
- inferFromTypes(getUnionType(valueTypes), getTemplateTypeFromMappedType(target));
15025
+ const propTypes = map(getPropertiesOfType(source), getTypeOfSymbol);
15026
+ const stringIndexType = getIndexTypeOfType(source, IndexKind.String);
15027
+ const numberIndexInfo = getNonEnumNumberIndexInfo(source);
15028
+ const numberIndexType = numberIndexInfo && numberIndexInfo.type;
15029
+ inferFromTypes(getUnionType(append(append(propTypes, stringIndexType), numberIndexType)), getTemplateTypeFromMappedType(target));
15010
15030
return true;
15011
15031
}
15012
15032
return false;
0 commit comments