@@ -260,6 +260,7 @@ namespace ts {
260260 const literalTypes = createMap<LiteralType>();
261261 const indexedAccessTypes = createMap<IndexedAccessType>();
262262 const evolvingArrayTypes: EvolvingArrayType[] = [];
263+ const undefinedProperties = createMap<Symbol>() as UnderscoreEscapedMap<Symbol>;
263264
264265 const unknownSymbol = createSymbol(SymbolFlags.Property, "unknown" as __String);
265266 const resolvingSymbol = createSymbol(0, InternalSymbolName.Resolving);
@@ -7984,7 +7985,7 @@ namespace ts {
79847985 const spread = createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
79857986 spread.flags |= propagatedFlags;
79867987 spread.flags |= TypeFlags.FreshLiteral | TypeFlags.ContainsObjectLiteral;
7987- (spread as ObjectType).objectFlags |= ObjectFlags.ObjectLiteral;
7988+ (spread as ObjectType).objectFlags |= ( ObjectFlags.ObjectLiteral | ObjectFlags.ContainsSpread) ;
79887989 spread.symbol = symbol;
79897990 return spread;
79907991 }
@@ -9026,7 +9027,7 @@ namespace ts {
90269027
90279028 if (isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True;
90289029
9029- if (getObjectFlags (source) & ObjectFlags.ObjectLiteral && source.flags & TypeFlags.FreshLiteral) {
9030+ if (isObjectLiteralType (source) && source.flags & TypeFlags.FreshLiteral) {
90309031 if (hasExcessProperties(<FreshObjectLiteralType>source, target, reportErrors)) {
90319032 if (reportErrors) {
90329033 reportRelationError(headMessage, source, target);
@@ -9596,14 +9597,27 @@ namespace ts {
95969597 if (relation === identityRelation) {
95979598 return propertiesIdenticalTo(source, target);
95989599 }
9599- const requireOptionalProperties = relation === subtypeRelation && !(getObjectFlags( source) & ObjectFlags.ObjectLiteral );
9600+ const requireOptionalProperties = relation === subtypeRelation && !isObjectLiteralType( source);
96009601 const unmatchedProperty = getUnmatchedProperty(source, target, requireOptionalProperties);
96019602 if (unmatchedProperty) {
96029603 if (reportErrors) {
96039604 reportError(Diagnostics.Property_0_is_missing_in_type_1, symbolToString(unmatchedProperty), typeToString(source));
96049605 }
96059606 return Ternary.False;
96069607 }
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+ }
96079621 let result = Ternary.True;
96089622 const properties = getPropertiesOfObjectType(target);
96099623 for (const targetProp of properties) {
@@ -10425,7 +10439,7 @@ namespace ts {
1042510439 * Leave signatures alone since they are not subject to the check.
1042610440 */
1042710441 function getRegularTypeOfObjectLiteral(type: Type): Type {
10428- if (!(getObjectFlags (type) & ObjectFlags.ObjectLiteral && type.flags & TypeFlags.FreshLiteral)) {
10442+ if (!(isObjectLiteralType (type) && type.flags & TypeFlags.FreshLiteral)) {
1042910443 return type;
1043010444 }
1043110445 const regularType = (<FreshObjectLiteralType>type).regularType;
@@ -10447,18 +10461,74 @@ namespace ts {
1044710461 return regularNew;
1044810462 }
1044910463
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 {
1045110502 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);
1045310505 return widened === original ? prop : createSymbolWithType(prop, widened);
1045410506 }
1045510507
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 {
1045710520 const members = createSymbolTable();
1045810521 for (const prop of getPropertiesOfObjectType(type)) {
1045910522 // Since get accessors already widen their return value there is no need to
1046010523 // 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+ }
1046210532 }
1046310533 const stringIndexInfo = getIndexInfoOfType(type, IndexKind.String);
1046410534 const numberIndexInfo = getIndexInfoOfType(type, IndexKind.Number);
@@ -10467,20 +10537,25 @@ namespace ts {
1046710537 numberIndexInfo && createIndexInfo(getWidenedType(numberIndexInfo.type), numberIndexInfo.isReadonly));
1046810538 }
1046910539
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 );
1047210542 }
1047310543
10474- function getWidenedType (type: Type): Type {
10544+ function getWidenedTypeWithContext (type: Type, context: WideningContext ): Type {
1047510545 if (type.flags & TypeFlags.RequiresWidening) {
1047610546 if (type.flags & TypeFlags.Nullable) {
1047710547 return anyType;
1047810548 }
10479- if (getObjectFlags (type) & ObjectFlags.ObjectLiteral ) {
10480- return getWidenedTypeOfObjectLiteral(type);
10549+ if (isObjectLiteralType (type)) {
10550+ return getWidenedTypeOfObjectLiteral(type, context );
1048110551 }
1048210552 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));
1048410559 }
1048510560 if (isArrayType(type) || isTupleType(type)) {
1048610561 return createTypeReference((<TypeReference>type).target, sameMap((<TypeReference>type).typeArguments, getWidenedType));
@@ -10502,28 +10577,35 @@ namespace ts {
1050210577 */
1050310578 function reportWideningErrorsInType(type: Type): boolean {
1050410579 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 )) {
1050810583 errorReported = true;
1050910584 }
10585+ else {
10586+ for (const t of (<UnionType>type).types) {
10587+ if (reportWideningErrorsInType(t)) {
10588+ errorReported = true;
10589+ }
10590+ }
10591+ }
1051010592 }
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+ }
1051610598 }
1051710599 }
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;
1052510608 }
10526- errorReported = true;
1052710609 }
1052810610 }
1052910611 }
@@ -11029,11 +11111,28 @@ namespace ts {
1102911111 return constraint && maybeTypeOfKind(constraint, TypeFlags.Primitive | TypeFlags.Index);
1103011112 }
1103111113
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+
1103211129 function getInferredType(context: InferenceContext, index: number): Type {
1103311130 const inference = context.inferences[index];
1103411131 let inferredType = inference.inferredType;
1103511132 if (!inferredType) {
1103611133 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);
1103711136 // We widen inferred literal types if
1103811137 // all inferences were made to top-level ocurrences of the type parameter, and
1103911138 // the type parameter has no constraint or its constraint includes no primitive or literal types, and
@@ -11042,7 +11141,7 @@ namespace ts {
1104211141 const widenLiteralTypes = inference.topLevel &&
1104311142 !hasPrimitiveConstraint(inference.typeParameter) &&
1104411143 (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;
1104611145 // If all inferences were made from contravariant positions, infer a common subtype. Otherwise, if
1104711146 // union types were requested or if all inferences were made from the return type position, infer a
1104811147 // union type. Otherwise, infer a common supertype.
0 commit comments