From 480b73915fdd805952fd355e4cf3e1bc803e0878 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Tue, 6 Aug 2019 15:03:24 -0700 Subject: [PATCH] Improve excess property checking for intersections (#32582) * Improve excess property checking for intersections Still a draft, the implementation needs improvement * Use mutable isIntersection in checkTypeRelatedTo This makes parameter lists a lot shorter. Seems like a slight improvement, although I can revert if I change my mind. * Fix semicolon lint * Remove TODOOOO * Revert "Use mutable isIntersection in checkTypeRelatedTo" This reverts commit b8dccff2a25495867cff070b94601015e689b8ff. --- src/compiler/checker.ts | 54 +++++++++--------- ...ropertyCheckWithNestedArrayIntersection.js | 34 +++++++++++ ...tyCheckWithNestedArrayIntersection.symbols | 56 +++++++++++++++++++ ...ertyCheckWithNestedArrayIntersection.types | 54 ++++++++++++++++++ ...tyChecksWithNestedIntersections.errors.txt | 2 +- ...ssPropertyChecksWithNestedIntersections.js | 4 +- ...pertyChecksWithNestedIntersections.symbols | 2 +- ...ropertyChecksWithNestedIntersections.types | 2 +- tests/baselines/reference/weakType.errors.txt | 16 ++---- tests/baselines/reference/weakType.js | 6 +- tests/baselines/reference/weakType.symbols | 10 ++-- tests/baselines/reference/weakType.types | 8 +-- ...ropertyCheckWithNestedArrayIntersection.ts | 22 ++++++++ ...ssPropertyChecksWithNestedIntersections.ts | 2 +- tests/cases/compiler/weakType.ts | 4 +- tests/cases/user/prettier/prettier | 2 +- 16 files changed, 220 insertions(+), 58 deletions(-) create mode 100644 tests/baselines/reference/excessPropertyCheckWithNestedArrayIntersection.js create mode 100644 tests/baselines/reference/excessPropertyCheckWithNestedArrayIntersection.symbols create mode 100644 tests/baselines/reference/excessPropertyCheckWithNestedArrayIntersection.types create mode 100644 tests/cases/compiler/excessPropertyCheckWithNestedArrayIntersection.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ab0d9b385f49f..30637c3cebcae 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12802,7 +12802,7 @@ namespace ts { isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True; const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes); - const isPerformingExcessPropertyChecks = (isObjectLiteralType(source) && getObjectFlags(source) & ObjectFlags.FreshLiteral); + const isPerformingExcessPropertyChecks = !isApparentIntersectionConstituent && (isObjectLiteralType(source) && getObjectFlags(source) & ObjectFlags.FreshLiteral); if (isPerformingExcessPropertyChecks) { const discriminantType = target.flags & TypeFlags.Union ? findMatchingDiscriminantType(source, target as UnionType) : undefined; if (hasExcessProperties(source, target, discriminantType, reportErrors)) { @@ -12813,11 +12813,11 @@ namespace ts { } } - if (relation !== comparableRelation && !isApparentIntersectionConstituent && + const isPerformingCommonPropertyChecks = relation !== comparableRelation && !isApparentIntersectionConstituent && source.flags & (TypeFlags.Primitive | TypeFlags.Object | TypeFlags.Intersection) && source !== globalObjectType && target.flags & (TypeFlags.Object | TypeFlags.Intersection) && isWeakType(target) && - (getPropertiesOfType(source).length > 0 || typeHasCallOrConstructSignatures(source)) && - !hasCommonProperties(source, target, isComparingJsxAttributes)) { + (getPropertiesOfType(source).length > 0 || typeHasCallOrConstructSignatures(source)); + if (isPerformingCommonPropertyChecks && !hasCommonProperties(source, target, isComparingJsxAttributes)) { if (reportErrors) { const calls = getSignaturesOfType(source, SignatureKind.Call); const constructs = getSignaturesOfType(source, SignatureKind.Construct); @@ -12847,10 +12847,10 @@ namespace ts { else { if (target.flags & TypeFlags.Union) { result = typeRelatedToSomeType(getRegularTypeOfObjectLiteral(source), target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive)); - if (result && isPerformingExcessPropertyChecks) { + if (result && (isPerformingExcessPropertyChecks || isPerformingCommonPropertyChecks)) { // Validate against excess props using the original `source` const discriminantType = findMatchingDiscriminantType(source, target as UnionType) || filterPrimitivesIfContainsNonPrimitive(target as UnionType); - if (!propertiesRelatedTo(source, discriminantType, reportErrors, /*excludedProperties*/ undefined)) { + if (!propertiesRelatedTo(source, discriminantType, reportErrors, /*excludedProperties*/ undefined, isIntersectionConstituent)) { return Ternary.False; } } @@ -12858,9 +12858,9 @@ namespace ts { else if (target.flags & TypeFlags.Intersection) { isIntersectionConstituent = true; // set here to affect the following trio of checks result = typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors); - if (result && isPerformingExcessPropertyChecks) { + if (result && (isPerformingExcessPropertyChecks || isPerformingCommonPropertyChecks)) { // Validate against excess props using the original `source` - if (!propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined)) { + if (!propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, /*isIntersectionConstituent*/ false)) { return Ternary.False; } } @@ -13186,7 +13186,7 @@ namespace ts { return result; } - function typeArgumentsRelatedTo(sources: ReadonlyArray = emptyArray, targets: ReadonlyArray = emptyArray, variances: ReadonlyArray = emptyArray, reportErrors: boolean): Ternary { + function typeArgumentsRelatedTo(sources: ReadonlyArray = emptyArray, targets: ReadonlyArray = emptyArray, variances: ReadonlyArray = emptyArray, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary { if (sources.length !== targets.length && relation === identityRelation) { return Ternary.False; } @@ -13210,10 +13210,10 @@ namespace ts { related = relation === identityRelation ? isRelatedTo(s, t, /*reportErrors*/ false) : compareTypesIdentical(s, t); } else if (variance === VarianceFlags.Covariant) { - related = isRelatedTo(s, t, reportErrors); + related = isRelatedTo(s, t, reportErrors, /*headMessage*/ undefined, isIntersectionConstituent); } else if (variance === VarianceFlags.Contravariant) { - related = isRelatedTo(t, s, reportErrors); + related = isRelatedTo(t, s, reportErrors, /*headMessage*/ undefined, isIntersectionConstituent); } else if (variance === VarianceFlags.Bivariant) { // In the bivariant case we first compare contravariantly without reporting @@ -13222,16 +13222,16 @@ namespace ts { // which is generally easier to reason about. related = isRelatedTo(t, s, /*reportErrors*/ false); if (!related) { - related = isRelatedTo(s, t, reportErrors); + related = isRelatedTo(s, t, reportErrors, /*headMessage*/ undefined, isIntersectionConstituent); } } else { // In the invariant case we first compare covariantly, and only when that // succeeds do we proceed to compare contravariantly. Thus, error elaboration // will typically be based on the covariant check. - related = isRelatedTo(s, t, reportErrors); + related = isRelatedTo(s, t, reportErrors, /*headMessage*/ undefined, isIntersectionConstituent); if (related) { - related &= isRelatedTo(t, s, reportErrors); + related &= isRelatedTo(t, s, reportErrors, /*headMessage*/ undefined, isIntersectionConstituent); } } if (!related) { @@ -13377,7 +13377,7 @@ namespace ts { source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol && !(source.aliasTypeArgumentsContainsMarker || target.aliasTypeArgumentsContainsMarker)) { const variances = getAliasVariances(source.aliasSymbol); - const varianceResult = relateVariances(source.aliasTypeArguments, target.aliasTypeArguments, variances); + const varianceResult = relateVariances(source.aliasTypeArguments, target.aliasTypeArguments, variances, isIntersectionConstituent); if (varianceResult !== undefined) { return varianceResult; } @@ -13563,7 +13563,7 @@ namespace ts { // type references (which are intended by be compared structurally). Obtain the variance // information for the type parameters and relate the type arguments accordingly. const variances = getVariances((source).target); - const varianceResult = relateVariances((source).typeArguments, (target).typeArguments, variances); + const varianceResult = relateVariances((source).typeArguments, (target).typeArguments, variances, isIntersectionConstituent); if (varianceResult !== undefined) { return varianceResult; } @@ -13591,7 +13591,7 @@ namespace ts { if (source.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Object) { // Report structural errors only if we haven't reported any errors yet const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo && !sourceIsPrimitive; - result = propertiesRelatedTo(source, target, reportStructuralErrors, /*excludedProperties*/ undefined); + result = propertiesRelatedTo(source, target, reportStructuralErrors, /*excludedProperties*/ undefined, isIntersectionConstituent); if (result) { result &= signaturesRelatedTo(source, target, SignatureKind.Call, reportStructuralErrors); if (result) { @@ -13627,8 +13627,8 @@ namespace ts { } return Ternary.False; - function relateVariances(sourceTypeArguments: ReadonlyArray | undefined, targetTypeArguments: ReadonlyArray | undefined, variances: VarianceFlags[]) { - if (result = typeArgumentsRelatedTo(sourceTypeArguments, targetTypeArguments, variances, reportErrors)) { + function relateVariances(sourceTypeArguments: ReadonlyArray | undefined, targetTypeArguments: ReadonlyArray | undefined, variances: VarianceFlags[], isIntersectionConstituent: boolean) { + if (result = typeArgumentsRelatedTo(sourceTypeArguments, targetTypeArguments, variances, reportErrors, isIntersectionConstituent)) { return result; } if (some(variances, v => !!(v & VarianceFlags.AllowsStructuralFallback))) { @@ -13756,7 +13756,7 @@ namespace ts { if (!targetProperty) continue outer; if (sourceProperty === targetProperty) continue; // We compare the source property to the target in the context of a single discriminant type. - const related = propertyRelatedTo(source, target, sourceProperty, targetProperty, _ => combination[i], /*reportErrors*/ false); + const related = propertyRelatedTo(source, target, sourceProperty, targetProperty, _ => combination[i], /*reportErrors*/ false, /*isIntersectionConstituent*/ false); // If the target property could not be found, or if the properties were not related, // then this constituent is not a match. if (!related) { @@ -13775,7 +13775,7 @@ namespace ts { // Compare the remaining non-discriminant properties of each match. let result = Ternary.True; for (const type of matchingTypes) { - result &= propertiesRelatedTo(source, type, /*reportErrors*/ false, excludedProperties); + result &= propertiesRelatedTo(source, type, /*reportErrors*/ false, excludedProperties, /*isIntersectionConstituent*/ false); if (result) { result &= signaturesRelatedTo(source, type, SignatureKind.Call, /*reportStructuralErrors*/ false); if (result) { @@ -13811,7 +13811,7 @@ namespace ts { return result || properties; } - function isPropertySymbolTypeRelated(sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean): Ternary { + function isPropertySymbolTypeRelated(sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary { const targetIsOptional = strictNullChecks && !!(getCheckFlags(targetProp) & CheckFlags.Partial); const source = getTypeOfSourceProperty(sourceProp); if (getCheckFlags(targetProp) & CheckFlags.DeferredType && !getSymbolLinks(targetProp).type) { @@ -13850,11 +13850,11 @@ namespace ts { return result; } else { - return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors); + return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors, /*headMessage*/ undefined, isIntersectionConstituent); } } - function propertyRelatedTo(source: Type, target: Type, sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean): Ternary { + function propertyRelatedTo(source: Type, target: Type, sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary { const sourcePropFlags = getDeclarationModifierFlagsFromSymbol(sourceProp); const targetPropFlags = getDeclarationModifierFlagsFromSymbol(targetProp); if (sourcePropFlags & ModifierFlags.Private || targetPropFlags & ModifierFlags.Private) { @@ -13896,7 +13896,7 @@ namespace ts { return Ternary.False; } // If the target comes from a partial union prop, allow `undefined` in the target type - const related = isPropertySymbolTypeRelated(sourceProp, targetProp, getTypeOfSourceProperty, reportErrors); + const related = isPropertySymbolTypeRelated(sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, isIntersectionConstituent); if (!related) { if (reportErrors) { reportError(Diagnostics.Types_of_property_0_are_incompatible, symbolToString(targetProp)); @@ -13921,7 +13921,7 @@ namespace ts { return related; } - function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean, excludedProperties: UnderscoreEscapedMap | undefined): Ternary { + function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean, excludedProperties: UnderscoreEscapedMap | undefined, isIntersectionConstituent: boolean): Ternary { if (relation === identityRelation) { return propertiesIdenticalTo(source, target, excludedProperties); } @@ -14008,7 +14008,7 @@ namespace ts { if (!(targetProp.flags & SymbolFlags.Prototype)) { const sourceProp = getPropertyOfType(source, targetProp.escapedName); if (sourceProp && sourceProp !== targetProp) { - const related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors); + const related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors, isIntersectionConstituent); if (!related) { return Ternary.False; } diff --git a/tests/baselines/reference/excessPropertyCheckWithNestedArrayIntersection.js b/tests/baselines/reference/excessPropertyCheckWithNestedArrayIntersection.js new file mode 100644 index 0000000000000..0f9085edd105d --- /dev/null +++ b/tests/baselines/reference/excessPropertyCheckWithNestedArrayIntersection.js @@ -0,0 +1,34 @@ +//// [excessPropertyCheckWithNestedArrayIntersection.ts] +interface ValueOnlyFields { + fields: Array<{ + value: number | null; + }>; +} +interface ValueAndKeyFields { + fields: Array<{ + key: string | null; + value: number | null; + }>; +} +interface BugRepro { + dataType: ValueAndKeyFields & ValueOnlyFields; +} +const repro: BugRepro = { + dataType: { + fields: [{ + key: 'bla', // should be OK: Not excess + value: null, + }], + } +} + + +//// [excessPropertyCheckWithNestedArrayIntersection.js] +var repro = { + dataType: { + fields: [{ + key: 'bla', + value: null + }] + } +}; diff --git a/tests/baselines/reference/excessPropertyCheckWithNestedArrayIntersection.symbols b/tests/baselines/reference/excessPropertyCheckWithNestedArrayIntersection.symbols new file mode 100644 index 0000000000000..7d1ec2afde4db --- /dev/null +++ b/tests/baselines/reference/excessPropertyCheckWithNestedArrayIntersection.symbols @@ -0,0 +1,56 @@ +=== tests/cases/compiler/excessPropertyCheckWithNestedArrayIntersection.ts === +interface ValueOnlyFields { +>ValueOnlyFields : Symbol(ValueOnlyFields, Decl(excessPropertyCheckWithNestedArrayIntersection.ts, 0, 0)) + + fields: Array<{ +>fields : Symbol(ValueOnlyFields.fields, Decl(excessPropertyCheckWithNestedArrayIntersection.ts, 0, 27)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + value: number | null; +>value : Symbol(value, Decl(excessPropertyCheckWithNestedArrayIntersection.ts, 1, 19)) + + }>; +} +interface ValueAndKeyFields { +>ValueAndKeyFields : Symbol(ValueAndKeyFields, Decl(excessPropertyCheckWithNestedArrayIntersection.ts, 4, 1)) + + fields: Array<{ +>fields : Symbol(ValueAndKeyFields.fields, Decl(excessPropertyCheckWithNestedArrayIntersection.ts, 5, 29)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + key: string | null; +>key : Symbol(key, Decl(excessPropertyCheckWithNestedArrayIntersection.ts, 6, 19)) + + value: number | null; +>value : Symbol(value, Decl(excessPropertyCheckWithNestedArrayIntersection.ts, 7, 27)) + + }>; +} +interface BugRepro { +>BugRepro : Symbol(BugRepro, Decl(excessPropertyCheckWithNestedArrayIntersection.ts, 10, 1)) + + dataType: ValueAndKeyFields & ValueOnlyFields; +>dataType : Symbol(BugRepro.dataType, Decl(excessPropertyCheckWithNestedArrayIntersection.ts, 11, 20)) +>ValueAndKeyFields : Symbol(ValueAndKeyFields, Decl(excessPropertyCheckWithNestedArrayIntersection.ts, 4, 1)) +>ValueOnlyFields : Symbol(ValueOnlyFields, Decl(excessPropertyCheckWithNestedArrayIntersection.ts, 0, 0)) +} +const repro: BugRepro = { +>repro : Symbol(repro, Decl(excessPropertyCheckWithNestedArrayIntersection.ts, 14, 5)) +>BugRepro : Symbol(BugRepro, Decl(excessPropertyCheckWithNestedArrayIntersection.ts, 10, 1)) + + dataType: { +>dataType : Symbol(dataType, Decl(excessPropertyCheckWithNestedArrayIntersection.ts, 14, 25)) + + fields: [{ +>fields : Symbol(fields, Decl(excessPropertyCheckWithNestedArrayIntersection.ts, 15, 13)) + + key: 'bla', // should be OK: Not excess +>key : Symbol(key, Decl(excessPropertyCheckWithNestedArrayIntersection.ts, 16, 14)) + + value: null, +>value : Symbol(value, Decl(excessPropertyCheckWithNestedArrayIntersection.ts, 17, 17)) + + }], + } +} + diff --git a/tests/baselines/reference/excessPropertyCheckWithNestedArrayIntersection.types b/tests/baselines/reference/excessPropertyCheckWithNestedArrayIntersection.types new file mode 100644 index 0000000000000..8b819992a0994 --- /dev/null +++ b/tests/baselines/reference/excessPropertyCheckWithNestedArrayIntersection.types @@ -0,0 +1,54 @@ +=== tests/cases/compiler/excessPropertyCheckWithNestedArrayIntersection.ts === +interface ValueOnlyFields { + fields: Array<{ +>fields : { value: number; }[] + + value: number | null; +>value : number +>null : null + + }>; +} +interface ValueAndKeyFields { + fields: Array<{ +>fields : { key: string; value: number; }[] + + key: string | null; +>key : string +>null : null + + value: number | null; +>value : number +>null : null + + }>; +} +interface BugRepro { + dataType: ValueAndKeyFields & ValueOnlyFields; +>dataType : ValueAndKeyFields & ValueOnlyFields +} +const repro: BugRepro = { +>repro : BugRepro +>{ dataType: { fields: [{ key: 'bla', // should be OK: Not excess value: null, }], }} : { dataType: { fields: { key: string; value: null; }[]; }; } + + dataType: { +>dataType : { fields: { key: string; value: null; }[]; } +>{ fields: [{ key: 'bla', // should be OK: Not excess value: null, }], } : { fields: { key: string; value: null; }[]; } + + fields: [{ +>fields : { key: string; value: null; }[] +>[{ key: 'bla', // should be OK: Not excess value: null, }] : { key: string; value: null; }[] +>{ key: 'bla', // should be OK: Not excess value: null, } : { key: string; value: null; } + + key: 'bla', // should be OK: Not excess +>key : string +>'bla' : "bla" + + value: null, +>value : null +>null : null + + }], + } +} + diff --git a/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.errors.txt b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.errors.txt index 368a71de003fc..32ee6b530f87d 100644 --- a/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.errors.txt +++ b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.errors.txt @@ -47,7 +47,7 @@ tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts(70,50): erro ~ !!! error TS2322: Type 'number' is not assignable to type 'string'. !!! related TS6500 tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts:4:5: The expected type comes from property 'x' which is declared here on type 'A' - let f: D = { a: { x: 'hello', y: 2 }, c: 5 }; // should be an error + let f: D = { a: { x: 'hello', y: 2 }, c: 5 }; // error - y does not exist in type A ~~~~ !!! error TS2322: Type '{ x: string; y: number; }' is not assignable to type 'A'. !!! error TS2322: Object literal may only specify known properties, and 'y' does not exist in type 'A'. diff --git a/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.js b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.js index f43b7bf83a47a..e242825b852da 100644 --- a/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.js +++ b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.js @@ -21,7 +21,7 @@ let c: B = { a: { x: 'hello', y: 2 } }; // error - y does not exist in type A let d: D = { a: { x: 'hello' }, c: 5 }; // ok let e: D = { a: { x: 2 }, c: 5 }; // error - types of property x are incompatible -let f: D = { a: { x: 'hello', y: 2 }, c: 5 }; // should be an error +let f: D = { a: { x: 'hello', y: 2 }, c: 5 }; // error - y does not exist in type A // https://github.com/Microsoft/TypeScript/issues/18075 @@ -80,7 +80,7 @@ var b = { a: { x: 2 } }; // error - types of property x are incompatible var c = { a: { x: 'hello', y: 2 } }; // error - y does not exist in type A var d = { a: { x: 'hello' }, c: 5 }; // ok var e = { a: { x: 2 }, c: 5 }; // error - types of property x are incompatible -var f = { a: { x: 'hello', y: 2 }, c: 5 }; // should be an error +var f = { a: { x: 'hello', y: 2 }, c: 5 }; // error - y does not exist in type A exports.photo = { id: 1, url: '', diff --git a/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.symbols b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.symbols index bf10195045e35..f859b8f48ef45 100644 --- a/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.symbols +++ b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.symbols @@ -61,7 +61,7 @@ let e: D = { a: { x: 2 }, c: 5 }; // error - types of property x are incompatibl >x : Symbol(x, Decl(excessPropertyChecksWithNestedIntersections.ts, 21, 17)) >c : Symbol(c, Decl(excessPropertyChecksWithNestedIntersections.ts, 21, 25)) -let f: D = { a: { x: 'hello', y: 2 }, c: 5 }; // should be an error +let f: D = { a: { x: 'hello', y: 2 }, c: 5 }; // error - y does not exist in type A >f : Symbol(f, Decl(excessPropertyChecksWithNestedIntersections.ts, 22, 3)) >D : Symbol(D, Decl(excessPropertyChecksWithNestedIntersections.ts, 12, 1)) >a : Symbol(a, Decl(excessPropertyChecksWithNestedIntersections.ts, 22, 12)) diff --git a/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.types b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.types index d15b8f7e0dded..29019339fa6a7 100644 --- a/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.types +++ b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.types @@ -65,7 +65,7 @@ let e: D = { a: { x: 2 }, c: 5 }; // error - types of property x are incompatibl >c : number >5 : 5 -let f: D = { a: { x: 'hello', y: 2 }, c: 5 }; // should be an error +let f: D = { a: { x: 'hello', y: 2 }, c: 5 }; // error - y does not exist in type A >f : D >{ a: { x: 'hello', y: 2 }, c: 5 } : { a: { x: string; y: number; }; c: number; } >a : { x: string; y: number; } diff --git a/tests/baselines/reference/weakType.errors.txt b/tests/baselines/reference/weakType.errors.txt index 4ea859a82dd09..9fc9deab703c3 100644 --- a/tests/baselines/reference/weakType.errors.txt +++ b/tests/baselines/reference/weakType.errors.txt @@ -5,10 +5,8 @@ tests/cases/compiler/weakType.ts(18,13): error TS2559: Type '12' has no properti tests/cases/compiler/weakType.ts(19,13): error TS2559: Type '"completely wrong"' has no properties in common with type 'Settings'. tests/cases/compiler/weakType.ts(20,13): error TS2559: Type 'false' has no properties in common with type 'Settings'. tests/cases/compiler/weakType.ts(37,18): error TS2559: Type '{ error?: number; }' has no properties in common with type 'ChangeOptions'. -tests/cases/compiler/weakType.ts(62,5): error TS2322: Type '{ properties: { wrong: string; }; }' is not assignable to type 'Weak & Spoiler'. - Type '{ properties: { wrong: string; }; }' is not assignable to type 'Weak'. - Types of property 'properties' are incompatible. - Type '{ wrong: string; }' has no properties in common with type '{ b?: number; }'. +tests/cases/compiler/weakType.ts(62,5): error TS2326: Types of property 'properties' are incompatible. + Type '{ wrong: string; }' has no properties in common with type '{ b?: number; }'. ==== tests/cases/compiler/weakType.ts (8 errors) ==== @@ -85,16 +83,14 @@ tests/cases/compiler/weakType.ts(62,5): error TS2322: Type '{ properties: { wron b?: number } } - declare let unknown: { + declare let propertiesWrong: { properties: { wrong: string } } - let weak: Weak & Spoiler = unknown + let weak: Weak & Spoiler = propertiesWrong ~~~~ -!!! error TS2322: Type '{ properties: { wrong: string; }; }' is not assignable to type 'Weak & Spoiler'. -!!! error TS2322: Type '{ properties: { wrong: string; }; }' is not assignable to type 'Weak'. -!!! error TS2322: Types of property 'properties' are incompatible. -!!! error TS2322: Type '{ wrong: string; }' has no properties in common with type '{ b?: number; }'. +!!! error TS2326: Types of property 'properties' are incompatible. +!!! error TS2326: Type '{ wrong: string; }' has no properties in common with type '{ b?: number; }'. \ No newline at end of file diff --git a/tests/baselines/reference/weakType.js b/tests/baselines/reference/weakType.js index 850d8de6a8b03..3bc20032d7fab 100644 --- a/tests/baselines/reference/weakType.js +++ b/tests/baselines/reference/weakType.js @@ -55,12 +55,12 @@ type Weak = { b?: number } } -declare let unknown: { +declare let propertiesWrong: { properties: { wrong: string } } -let weak: Weak & Spoiler = unknown +let weak: Weak & Spoiler = propertiesWrong @@ -89,4 +89,4 @@ var K = /** @class */ (function () { return K; }()); var ctor = K; -var weak = unknown; +var weak = propertiesWrong; diff --git a/tests/baselines/reference/weakType.symbols b/tests/baselines/reference/weakType.symbols index 3e47c8281903d..0b51b726517b0 100644 --- a/tests/baselines/reference/weakType.symbols +++ b/tests/baselines/reference/weakType.symbols @@ -144,20 +144,20 @@ type Weak = { >b : Symbol(b, Decl(weakType.ts, 52, 18)) } } -declare let unknown: { ->unknown : Symbol(unknown, Decl(weakType.ts, 56, 11)) +declare let propertiesWrong: { +>propertiesWrong : Symbol(propertiesWrong, Decl(weakType.ts, 56, 11)) properties: { ->properties : Symbol(properties, Decl(weakType.ts, 56, 22)) +>properties : Symbol(properties, Decl(weakType.ts, 56, 30)) wrong: string >wrong : Symbol(wrong, Decl(weakType.ts, 57, 17)) } } -let weak: Weak & Spoiler = unknown +let weak: Weak & Spoiler = propertiesWrong >weak : Symbol(weak, Decl(weakType.ts, 61, 3)) >Weak : Symbol(Weak, Decl(weakType.ts, 49, 32)) >Spoiler : Symbol(Spoiler, Decl(weakType.ts, 47, 18)) ->unknown : Symbol(unknown, Decl(weakType.ts, 56, 11)) +>propertiesWrong : Symbol(propertiesWrong, Decl(weakType.ts, 56, 11)) diff --git a/tests/baselines/reference/weakType.types b/tests/baselines/reference/weakType.types index 3a7d14a3724a7..58438c8dca223 100644 --- a/tests/baselines/reference/weakType.types +++ b/tests/baselines/reference/weakType.types @@ -147,8 +147,8 @@ type Weak = { >b : number } } -declare let unknown: { ->unknown : { properties: { wrong: string; }; } +declare let propertiesWrong: { +>propertiesWrong : { properties: { wrong: string; }; } properties: { >properties : { wrong: string; } @@ -157,8 +157,8 @@ declare let unknown: { >wrong : string } } -let weak: Weak & Spoiler = unknown +let weak: Weak & Spoiler = propertiesWrong >weak : Weak & Spoiler ->unknown : { properties: { wrong: string; }; } +>propertiesWrong : { properties: { wrong: string; }; } diff --git a/tests/cases/compiler/excessPropertyCheckWithNestedArrayIntersection.ts b/tests/cases/compiler/excessPropertyCheckWithNestedArrayIntersection.ts new file mode 100644 index 0000000000000..6da67d5d22bda --- /dev/null +++ b/tests/cases/compiler/excessPropertyCheckWithNestedArrayIntersection.ts @@ -0,0 +1,22 @@ +interface ValueOnlyFields { + fields: Array<{ + value: number | null; + }>; +} +interface ValueAndKeyFields { + fields: Array<{ + key: string | null; + value: number | null; + }>; +} +interface BugRepro { + dataType: ValueAndKeyFields & ValueOnlyFields; +} +const repro: BugRepro = { + dataType: { + fields: [{ + key: 'bla', // should be OK: Not excess + value: null, + }], + } +} diff --git a/tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts b/tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts index 86bf8f6261c96..b3e877e173a52 100644 --- a/tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts +++ b/tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts @@ -20,7 +20,7 @@ let c: B = { a: { x: 'hello', y: 2 } }; // error - y does not exist in type A let d: D = { a: { x: 'hello' }, c: 5 }; // ok let e: D = { a: { x: 2 }, c: 5 }; // error - types of property x are incompatible -let f: D = { a: { x: 'hello', y: 2 }, c: 5 }; // should be an error +let f: D = { a: { x: 'hello', y: 2 }, c: 5 }; // error - y does not exist in type A // https://github.com/Microsoft/TypeScript/issues/18075 diff --git a/tests/cases/compiler/weakType.ts b/tests/cases/compiler/weakType.ts index 08c9d95e672e6..d9415fdef45e6 100644 --- a/tests/cases/compiler/weakType.ts +++ b/tests/cases/compiler/weakType.ts @@ -54,10 +54,10 @@ type Weak = { b?: number } } -declare let unknown: { +declare let propertiesWrong: { properties: { wrong: string } } -let weak: Weak & Spoiler = unknown +let weak: Weak & Spoiler = propertiesWrong diff --git a/tests/cases/user/prettier/prettier b/tests/cases/user/prettier/prettier index 1e471a007968b..6fae09b67ec6f 160000 --- a/tests/cases/user/prettier/prettier +++ b/tests/cases/user/prettier/prettier @@ -1 +1 @@ -Subproject commit 1e471a007968b7490563b91ed6909ae6046f3fe8 +Subproject commit 6fae09b67ec6f7b27259a5c133a6961d05a4161b