@@ -260,6 +260,7 @@ namespace ts {
260
260
const literalTypes = createMap<LiteralType>();
261
261
const indexedAccessTypes = createMap<IndexedAccessType>();
262
262
const evolvingArrayTypes: EvolvingArrayType[] = [];
263
+ const undefinedProperties = createMap<Symbol>() as UnderscoreEscapedMap<Symbol>;
263
264
264
265
const unknownSymbol = createSymbol(SymbolFlags.Property, "unknown" as __String);
265
266
const resolvingSymbol = createSymbol(0, InternalSymbolName.Resolving);
@@ -7984,7 +7985,7 @@ namespace ts {
7984
7985
const spread = createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
7985
7986
spread.flags |= propagatedFlags;
7986
7987
spread.flags |= TypeFlags.FreshLiteral | TypeFlags.ContainsObjectLiteral;
7987
- (spread as ObjectType).objectFlags |= ObjectFlags.ObjectLiteral;
7988
+ (spread as ObjectType).objectFlags |= ( ObjectFlags.ObjectLiteral | ObjectFlags.ContainsSpread) ;
7988
7989
spread.symbol = symbol;
7989
7990
return spread;
7990
7991
}
@@ -9026,7 +9027,7 @@ namespace ts {
9026
9027
9027
9028
if (isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True;
9028
9029
9029
- if (getObjectFlags (source) & ObjectFlags.ObjectLiteral && source.flags & TypeFlags.FreshLiteral) {
9030
+ if (isObjectLiteralType (source) && source.flags & TypeFlags.FreshLiteral) {
9030
9031
if (hasExcessProperties(<FreshObjectLiteralType>source, target, reportErrors)) {
9031
9032
if (reportErrors) {
9032
9033
reportRelationError(headMessage, source, target);
@@ -9596,14 +9597,27 @@ namespace ts {
9596
9597
if (relation === identityRelation) {
9597
9598
return propertiesIdenticalTo(source, target);
9598
9599
}
9599
- const requireOptionalProperties = relation === subtypeRelation && !(getObjectFlags( source) & ObjectFlags.ObjectLiteral );
9600
+ const requireOptionalProperties = relation === subtypeRelation && !isObjectLiteralType( source);
9600
9601
const unmatchedProperty = getUnmatchedProperty(source, target, requireOptionalProperties);
9601
9602
if (unmatchedProperty) {
9602
9603
if (reportErrors) {
9603
9604
reportError(Diagnostics.Property_0_is_missing_in_type_1, symbolToString(unmatchedProperty), typeToString(source));
9604
9605
}
9605
9606
return Ternary.False;
9606
9607
}
9608
+ if (isObjectLiteralType(target)) {
9609
+ for (const sourceProp of getPropertiesOfType(source)) {
9610
+ if (!getPropertyOfObjectType(target, sourceProp.escapedName)) {
9611
+ const sourceType = getTypeOfSymbol(sourceProp);
9612
+ if (!(sourceType === undefinedType || sourceType === undefinedWideningType)) {
9613
+ if (reportErrors) {
9614
+ reportError(Diagnostics.Property_0_does_not_exist_on_type_1, symbolToString(sourceProp), typeToString(target));
9615
+ }
9616
+ return Ternary.False;
9617
+ }
9618
+ }
9619
+ }
9620
+ }
9607
9621
let result = Ternary.True;
9608
9622
const properties = getPropertiesOfObjectType(target);
9609
9623
for (const targetProp of properties) {
@@ -10425,7 +10439,7 @@ namespace ts {
10425
10439
* Leave signatures alone since they are not subject to the check.
10426
10440
*/
10427
10441
function getRegularTypeOfObjectLiteral(type: Type): Type {
10428
- if (!(getObjectFlags (type) & ObjectFlags.ObjectLiteral && type.flags & TypeFlags.FreshLiteral)) {
10442
+ if (!(isObjectLiteralType (type) && type.flags & TypeFlags.FreshLiteral)) {
10429
10443
return type;
10430
10444
}
10431
10445
const regularType = (<FreshObjectLiteralType>type).regularType;
@@ -10447,18 +10461,74 @@ namespace ts {
10447
10461
return regularNew;
10448
10462
}
10449
10463
10450
- function getWidenedProperty(prop: Symbol): Symbol {
10464
+ function createWideningContext(parent: WideningContext, propertyName: __String, siblings: Type[]): WideningContext {
10465
+ return { parent, propertyName, siblings, resolvedPropertyNames: undefined };
10466
+ }
10467
+
10468
+ function getSiblingsOfContext(context: WideningContext): Type[] {
10469
+ if (!context.siblings) {
10470
+ const siblings: Type[] = [];
10471
+ for (const type of getSiblingsOfContext(context.parent)) {
10472
+ if (isObjectLiteralType(type)) {
10473
+ const prop = getPropertyOfObjectType(type, context.propertyName);
10474
+ if (prop) {
10475
+ forEachType(getTypeOfSymbol(prop), t => {
10476
+ siblings.push(t);
10477
+ });
10478
+ }
10479
+ }
10480
+ }
10481
+ context.siblings = siblings;
10482
+ }
10483
+ return context.siblings;
10484
+ }
10485
+
10486
+ function getPropertyNamesOfContext(context: WideningContext): __String[] {
10487
+ if (!context.resolvedPropertyNames) {
10488
+ const names = createMap<boolean>() as UnderscoreEscapedMap<boolean>;
10489
+ for (const t of getSiblingsOfContext(context)) {
10490
+ if (isObjectLiteralType(t) && !(getObjectFlags(t) & ObjectFlags.ContainsSpread)) {
10491
+ for (const prop of getPropertiesOfType(t)) {
10492
+ names.set(prop.escapedName, true);
10493
+ }
10494
+ }
10495
+ }
10496
+ context.resolvedPropertyNames = arrayFrom(names.keys());
10497
+ }
10498
+ return context.resolvedPropertyNames;
10499
+ }
10500
+
10501
+ function getWidenedProperty(prop: Symbol, context: WideningContext): Symbol {
10451
10502
const original = getTypeOfSymbol(prop);
10452
- const widened = getWidenedType(original);
10503
+ const propContext = context && createWideningContext(context, prop.escapedName, /*siblings*/ undefined);
10504
+ const widened = getWidenedTypeWithContext(original, propContext);
10453
10505
return widened === original ? prop : createSymbolWithType(prop, widened);
10454
10506
}
10455
10507
10456
- function getWidenedTypeOfObjectLiteral(type: Type): Type {
10508
+ function getUndefinedProperty(name: __String) {
10509
+ const cached = undefinedProperties.get(name);
10510
+ if (cached) {
10511
+ return cached;
10512
+ }
10513
+ const result = createSymbol(SymbolFlags.Property | SymbolFlags.Optional, name);
10514
+ result.type = undefinedType;
10515
+ undefinedProperties.set(name, result);
10516
+ return result;
10517
+ }
10518
+
10519
+ function getWidenedTypeOfObjectLiteral(type: Type, context: WideningContext): Type {
10457
10520
const members = createSymbolTable();
10458
10521
for (const prop of getPropertiesOfObjectType(type)) {
10459
10522
// Since get accessors already widen their return value there is no need to
10460
10523
// widen accessor based properties here.
10461
- members.set(prop.escapedName, prop.flags & SymbolFlags.Property ? getWidenedProperty(prop) : prop);
10524
+ members.set(prop.escapedName, prop.flags & SymbolFlags.Property ? getWidenedProperty(prop, context) : prop);
10525
+ }
10526
+ if (context) {
10527
+ for (const name of getPropertyNamesOfContext(context)) {
10528
+ if (!members.has(name)) {
10529
+ members.set(name, getUndefinedProperty(name));
10530
+ }
10531
+ }
10462
10532
}
10463
10533
const stringIndexInfo = getIndexInfoOfType(type, IndexKind.String);
10464
10534
const numberIndexInfo = getIndexInfoOfType(type, IndexKind.Number);
@@ -10467,20 +10537,25 @@ namespace ts {
10467
10537
numberIndexInfo && createIndexInfo(getWidenedType(numberIndexInfo.type), numberIndexInfo.isReadonly));
10468
10538
}
10469
10539
10470
- function getWidenedConstituentType (type: Type): Type {
10471
- return type.flags & TypeFlags.Nullable ? type : getWidenedType(type );
10540
+ function getWidenedType (type: Type) {
10541
+ return getWidenedTypeWithContext( type, /*context*/ undefined );
10472
10542
}
10473
10543
10474
- function getWidenedType (type: Type): Type {
10544
+ function getWidenedTypeWithContext (type: Type, context: WideningContext ): Type {
10475
10545
if (type.flags & TypeFlags.RequiresWidening) {
10476
10546
if (type.flags & TypeFlags.Nullable) {
10477
10547
return anyType;
10478
10548
}
10479
- if (getObjectFlags (type) & ObjectFlags.ObjectLiteral ) {
10480
- return getWidenedTypeOfObjectLiteral(type);
10549
+ if (isObjectLiteralType (type)) {
10550
+ return getWidenedTypeOfObjectLiteral(type, context );
10481
10551
}
10482
10552
if (type.flags & TypeFlags.Union) {
10483
- return getUnionType(sameMap((<UnionType>type).types, getWidenedConstituentType));
10553
+ const unionContext = context || createWideningContext(/*parent*/ undefined, /*propertyName*/ undefined, (<UnionType>type).types);
10554
+ const widenedTypes = sameMap((<UnionType>type).types, t => t.flags & TypeFlags.Nullable ? t : getWidenedTypeWithContext(t, unionContext));
10555
+ // Widening an empty object literal transitions from a highly restrictive type to
10556
+ // a highly inclusive one. For that reason we perform subtype reduction here if the
10557
+ // union includes empty object types (e.g. reducing {} | string to just {}).
10558
+ return getUnionType(widenedTypes, some(widenedTypes, isEmptyObjectType));
10484
10559
}
10485
10560
if (isArrayType(type) || isTupleType(type)) {
10486
10561
return createTypeReference((<TypeReference>type).target, sameMap((<TypeReference>type).typeArguments, getWidenedType));
@@ -10502,28 +10577,35 @@ namespace ts {
10502
10577
*/
10503
10578
function reportWideningErrorsInType(type: Type): boolean {
10504
10579
let errorReported = false;
10505
- if (type.flags & TypeFlags.Union ) {
10506
- for (const t of (<UnionType>type).types ) {
10507
- if (reportWideningErrorsInType(t )) {
10580
+ if (type.flags & TypeFlags.ContainsWideningType ) {
10581
+ if (type.flags & TypeFlags.Union ) {
10582
+ if (some((<UnionType>type).types, isEmptyObjectType )) {
10508
10583
errorReported = true;
10509
10584
}
10585
+ else {
10586
+ for (const t of (<UnionType>type).types) {
10587
+ if (reportWideningErrorsInType(t)) {
10588
+ errorReported = true;
10589
+ }
10590
+ }
10591
+ }
10510
10592
}
10511
- }
10512
- if (isArrayType(type) || isTupleType( type)) {
10513
- for (const t of (<TypeReference>type).typeArguments ) {
10514
- if (reportWideningErrorsInType(t)) {
10515
- errorReported = true;
10593
+ if (isArrayType(type) || isTupleType(type)) {
10594
+ for (const t of (<TypeReference> type).typeArguments ) {
10595
+ if (reportWideningErrorsInType(t) ) {
10596
+ errorReported = true;
10597
+ }
10516
10598
}
10517
10599
}
10518
- }
10519
- if (getObjectFlags(type) & ObjectFlags.ObjectLiteral) {
10520
- for (const p of getPropertiesOfObjectType(type)) {
10521
- const t = getTypeOfSymbol(p);
10522
- if (t.flags & TypeFlags.ContainsWideningType) {
10523
- if (!reportWideningErrorsInType(t)) {
10524
- error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, symbolName(p), typeToString(getWidenedType(t)));
10600
+ if (isObjectLiteralType(type)) {
10601
+ for (const p of getPropertiesOfObjectType(type)) {
10602
+ const t = getTypeOfSymbol(p);
10603
+ if (t.flags & TypeFlags.ContainsWideningType) {
10604
+ if (!reportWideningErrorsInType(t)) {
10605
+ error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, symbolName(p), typeToString(getWidenedType(t)));
10606
+ }
10607
+ errorReported = true;
10525
10608
}
10526
- errorReported = true;
10527
10609
}
10528
10610
}
10529
10611
}
@@ -11029,11 +11111,28 @@ namespace ts {
11029
11111
return constraint && maybeTypeOfKind(constraint, TypeFlags.Primitive | TypeFlags.Index);
11030
11112
}
11031
11113
11114
+ function isObjectLiteralType(type: Type) {
11115
+ return !!(getObjectFlags(type) & ObjectFlags.ObjectLiteral);
11116
+ }
11117
+
11118
+ function widenObjectLiteralCandidates(candidates: Type[]): Type[] {
11119
+ if (candidates.length > 1) {
11120
+ const objectLiterals = filter(candidates, isObjectLiteralType);
11121
+ if (objectLiterals.length) {
11122
+ const objectLiteralsType = getWidenedType(getUnionType(objectLiterals, /*subtypeReduction*/ true));
11123
+ return concatenate(filter(candidates, t => !isObjectLiteralType(t)), [objectLiteralsType]);
11124
+ }
11125
+ }
11126
+ return candidates;
11127
+ }
11128
+
11032
11129
function getInferredType(context: InferenceContext, index: number): Type {
11033
11130
const inference = context.inferences[index];
11034
11131
let inferredType = inference.inferredType;
11035
11132
if (!inferredType) {
11036
11133
if (inference.candidates) {
11134
+ // Extract all object literal types and replace them with a single widened and normalized type.
11135
+ const candidates = widenObjectLiteralCandidates(inference.candidates);
11037
11136
// We widen inferred literal types if
11038
11137
// all inferences were made to top-level ocurrences of the type parameter, and
11039
11138
// the type parameter has no constraint or its constraint includes no primitive or literal types, and
@@ -11042,7 +11141,7 @@ namespace ts {
11042
11141
const widenLiteralTypes = inference.topLevel &&
11043
11142
!hasPrimitiveConstraint(inference.typeParameter) &&
11044
11143
(inference.isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), inference.typeParameter));
11045
- const baseCandidates = widenLiteralTypes ? sameMap(inference. candidates, getWidenedLiteralType) : inference. candidates;
11144
+ const baseCandidates = widenLiteralTypes ? sameMap(candidates, getWidenedLiteralType) : candidates;
11046
11145
// If all inferences were made from contravariant positions, infer a common subtype. Otherwise, if
11047
11146
// union types were requested or if all inferences were made from the return type position, infer a
11048
11147
// union type. Otherwise, infer a common supertype.
0 commit comments