From 169e485d9096dcc32daddc1f18ccf9ee39164349 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 16 Apr 2019 21:58:48 -0700 Subject: [PATCH] Perform excess property checking on intersection and union members (#30853) * Perform excess property checking on intersection and union members * Allow partial union props to contain the undefined type * Add test case from #30771 * Un-terse getPossiblePropertiesOfUnionType side-effecting code * Fix bug exposed in RWC * Cache results of getPossiblePropertiesOfUnionType * Fix whitespace --- src/compiler/checker.ts | 96 +++++---- src/compiler/types.ts | 28 +-- ...heckingWhenTargetIsIntersection.errors.txt | 48 +++++ ...ropertyCheckingWhenTargetIsIntersection.js | 39 ++++ ...tyCheckingWhenTargetIsIntersection.symbols | 71 +++++++ ...ertyCheckingWhenTargetIsIntersection.types | 70 ++++++ .../excessPropertyCheckWithUnions.errors.txt | 16 +- ...tyChecksWithNestedIntersections.errors.txt | 116 ++++++++++ ...ssPropertyChecksWithNestedIntersections.js | 100 +++++++++ ...pertyChecksWithNestedIntersections.symbols | 190 +++++++++++++++++ ...ropertyChecksWithNestedIntersections.types | 201 ++++++++++++++++++ ...tUnionNestedExcessPropertyCheck.errors.txt | 44 ++++ ...nonObjectUnionNestedExcessPropertyCheck.js | 29 +++ ...jectUnionNestedExcessPropertyCheck.symbols | 46 ++++ ...ObjectUnionNestedExcessPropertyCheck.types | 49 +++++ ...tyCheckNoApparentPropTypeMismatchErrors.js | 17 ++ ...ckNoApparentPropTypeMismatchErrors.symbols | 41 ++++ ...heckNoApparentPropTypeMismatchErrors.types | 33 +++ .../unionExcessPropsWithPartialMember.js | 31 +++ .../unionExcessPropsWithPartialMember.symbols | 35 +++ .../unionExcessPropsWithPartialMember.types | 34 +++ ...ropertyCheckingWhenTargetIsIntersection.ts | 27 +++ ...ssPropertyChecksWithNestedIntersections.ts | 70 ++++++ ...nonObjectUnionNestedExcessPropertyCheck.ts | 19 ++ ...tyCheckNoApparentPropTypeMismatchErrors.ts | 11 + .../unionExcessPropsWithPartialMember.ts | 15 ++ 26 files changed, 1414 insertions(+), 62 deletions(-) create mode 100644 tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.errors.txt create mode 100644 tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.js create mode 100644 tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.symbols create mode 100644 tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.types create mode 100644 tests/baselines/reference/excessPropertyChecksWithNestedIntersections.errors.txt create mode 100644 tests/baselines/reference/excessPropertyChecksWithNestedIntersections.js create mode 100644 tests/baselines/reference/excessPropertyChecksWithNestedIntersections.symbols create mode 100644 tests/baselines/reference/excessPropertyChecksWithNestedIntersections.types create mode 100644 tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.errors.txt create mode 100644 tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.js create mode 100644 tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.symbols create mode 100644 tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.types create mode 100644 tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.js create mode 100644 tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.symbols create mode 100644 tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.types create mode 100644 tests/baselines/reference/unionExcessPropsWithPartialMember.js create mode 100644 tests/baselines/reference/unionExcessPropsWithPartialMember.symbols create mode 100644 tests/baselines/reference/unionExcessPropsWithPartialMember.types create mode 100644 tests/cases/compiler/deepExcessPropertyCheckingWhenTargetIsIntersection.ts create mode 100644 tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts create mode 100644 tests/cases/compiler/nonObjectUnionNestedExcessPropertyCheck.ts create mode 100644 tests/cases/compiler/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts create mode 100644 tests/cases/compiler/unionExcessPropsWithPartialMember.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0cb40d8541..0c91e1ba06 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7473,6 +7473,25 @@ namespace ts { return type.resolvedProperties; } + function getPossiblePropertiesOfUnionType(type: UnionType): Symbol[] { + if (type.possiblePropertyCache) { + return type.possiblePropertyCache.size ? arrayFrom(type.possiblePropertyCache.values()) : emptyArray; + } + type.possiblePropertyCache = createSymbolTable(); + for (const t of type.types) { + for (const p of getPropertiesOfType(t)) { + if (!type.possiblePropertyCache.has(p.escapedName)) { + const prop = getUnionOrIntersectionProperty(type, p.escapedName); + if (prop) { + type.possiblePropertyCache.set(p.escapedName, prop); + } + } + } + } + // We can't simply use the normal property cache here, since that will contain cached apparent type members :( + return type.possiblePropertyCache.size ? arrayFrom(type.possiblePropertyCache.values()) : emptyArray; + } + function getPropertiesOfType(type: Type): Symbol[] { type = getApparentType(type); return type.flags & TypeFlags.UnionOrIntersection ? @@ -7830,7 +7849,7 @@ namespace ts { const isUnion = containingType.flags & TypeFlags.Union; const excludeModifiers = isUnion ? ModifierFlags.NonPublicAccessibilityModifier : 0; // Flags we want to propagate to the result if they exist in all source symbols - let commonFlags = isUnion ? SymbolFlags.None : SymbolFlags.Optional; + let optionalFlag = isUnion ? SymbolFlags.None : SymbolFlags.Optional; let syntheticFlag = CheckFlags.SyntheticMethod; let checkFlags = 0; for (const current of containingType.types) { @@ -7839,7 +7858,12 @@ namespace ts { const prop = getPropertyOfType(type, name); const modifiers = prop ? getDeclarationModifierFlagsFromSymbol(prop) : 0; if (prop && !(modifiers & excludeModifiers)) { - commonFlags &= prop.flags; + if (isUnion) { + optionalFlag |= (prop.flags & SymbolFlags.Optional); + } + else { + optionalFlag &= prop.flags; + } const id = "" + getSymbolId(prop); if (!propSet.has(id)) { propSet.set(id, prop); @@ -7857,10 +7881,11 @@ namespace ts { const indexInfo = !isLateBoundName(name) && (isNumericLiteralName(name) && getIndexInfoOfType(type, IndexKind.Number) || getIndexInfoOfType(type, IndexKind.String)); if (indexInfo) { checkFlags |= indexInfo.isReadonly ? CheckFlags.Readonly : 0; + checkFlags |= CheckFlags.WritePartial; indexTypes = append(indexTypes, isTupleType(type) ? getRestTypeOfTupleType(type) || undefinedType : indexInfo.type); } else { - checkFlags |= CheckFlags.Partial; + checkFlags |= CheckFlags.ReadPartial; } } } @@ -7869,7 +7894,7 @@ namespace ts { return undefined; } const props = arrayFrom(propSet.values()); - if (props.length === 1 && !(checkFlags & CheckFlags.Partial) && !indexTypes) { + if (props.length === 1 && !(checkFlags & CheckFlags.ReadPartial) && !indexTypes) { return props[0]; } let declarations: Declaration[] | undefined; @@ -7900,7 +7925,7 @@ namespace ts { propTypes.push(type); } addRange(propTypes, indexTypes); - const result = createSymbol(SymbolFlags.Property | commonFlags, name, syntheticFlag | checkFlags); + const result = createSymbol(SymbolFlags.Property | optionalFlag, name, syntheticFlag | checkFlags); result.containingType = containingType; if (!hasNonUniformValueDeclaration && firstValueDeclaration) { result.valueDeclaration = firstValueDeclaration; @@ -7937,7 +7962,7 @@ namespace ts { function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: __String): Symbol | undefined { const property = getUnionOrIntersectionProperty(type, name); // We need to filter out partial properties in union types - return property && !(getCheckFlags(property) & CheckFlags.Partial) ? property : undefined; + return property && !(getCheckFlags(property) & CheckFlags.ReadPartial) ? property : undefined; } /** @@ -12276,25 +12301,6 @@ namespace ts { return true; } - function isUnionOrIntersectionTypeWithoutNullableConstituents(type: Type): boolean { - if (!(type.flags & TypeFlags.UnionOrIntersection)) { - return false; - } - // at this point we know that this is union or intersection type possibly with nullable constituents. - // check if we still will have compound type if we ignore nullable components. - let seenNonNullable = false; - for (const t of (type).types) { - if (t.flags & TypeFlags.Nullable) { - continue; - } - if (seenNonNullable) { - return true; - } - seenNonNullable = true; - } - return false; - } - /** * Compare two types and return * * Ternary.True if they are related with no assumptions, @@ -12349,7 +12355,8 @@ namespace ts { isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True; const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes); - if (isObjectLiteralType(source) && getObjectFlags(source) & ObjectFlags.FreshLiteral) { + const isPerformingExcessPropertyChecks = (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)) { if (reportErrors) { @@ -12357,13 +12364,6 @@ namespace ts { } return Ternary.False; } - // Above we check for excess properties with respect to the entire target type. When union - // and intersection types are further deconstructed on the target side, we don't want to - // make the check again (as it might fail for a partial target type). Therefore we obtain - // the regular source type and proceed with that. - if (isUnionOrIntersectionTypeWithoutNullableConstituents(target) && !discriminantType) { - source = getRegularTypeOfObjectLiteral(source); - } } if (relation !== comparableRelation && !isApparentIntersectionConstituent && @@ -12399,11 +12399,24 @@ namespace ts { } else { if (target.flags & TypeFlags.Union) { - result = typeRelatedToSomeType(source, target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive)); + result = typeRelatedToSomeType(getRegularTypeOfObjectLiteral(source), target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive)); + if (result && isPerformingExcessPropertyChecks) { + // Validate against excess props using the original `source` + const discriminantType = target.flags & TypeFlags.Union ? findMatchingDiscriminantType(source, target as UnionType) : undefined; + if (!propertiesRelatedTo(source, discriminantType || target, reportErrors)) { + return Ternary.False; + } + } } else if (target.flags & TypeFlags.Intersection) { isIntersectionConstituent = true; // set here to affect the following trio of checks - result = typeRelatedToEachType(source, target as IntersectionType, reportErrors); + result = typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors); + if (result && isPerformingExcessPropertyChecks) { + // Validate against excess props using the original `source` + if (!propertiesRelatedTo(source, target, reportErrors)) { + return Ternary.False; + } + } } else if (source.flags & TypeFlags.Intersection) { // Check to see if any constituents of the intersection are immediately related to the target. @@ -12506,7 +12519,7 @@ namespace ts { // check excess properties against discriminant type only, not the entire union return hasExcessProperties(source, discriminant, /*discriminant*/ undefined, reportErrors); } - for (const prop of getPropertiesOfObjectType(source)) { + for (const prop of getPropertiesOfType(source)) { if (shouldCheckAsExcessProperty(prop, source.symbol) && !isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) { if (reportErrors) { // Report error in terms of object types in the target as those are the only ones @@ -13233,7 +13246,9 @@ namespace ts { } } } - const properties = getPropertiesOfObjectType(target); + // 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_ + // from the target union, across all members + const properties = target.flags & TypeFlags.Union ? getPossiblePropertiesOfUnionType(target as UnionType) : getPropertiesOfType(target); for (const targetProp of properties) { if (!(targetProp.flags & SymbolFlags.Prototype)) { const sourceProp = getPropertyOfType(source, targetProp.escapedName); @@ -13281,7 +13296,8 @@ namespace ts { } return Ternary.False; } - const related = isRelatedTo(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp), reportErrors); + // If the target comes from a partial union prop, allow `undefined` in the target type + const related = isRelatedTo(getTypeOfSymbol(sourceProp), addOptionality(getTypeOfSymbol(targetProp), !!(getCheckFlags(targetProp) & CheckFlags.Partial)), reportErrors); if (!related) { if (reportErrors) { reportError(Diagnostics.Types_of_property_0_are_incompatible, symbolToString(targetProp)); @@ -14627,9 +14643,9 @@ namespace ts { } function* getUnmatchedProperties(source: Type, target: Type, requireOptionalProperties: boolean, matchDiscriminantProperties: boolean) { - const properties = target.flags & TypeFlags.Intersection ? getPropertiesOfUnionOrIntersectionType(target) : getPropertiesOfObjectType(target); + const properties = target.flags & TypeFlags.Union ? getPossiblePropertiesOfUnionType(target as UnionType) : getPropertiesOfType(target); for (const targetProp of properties) { - if (requireOptionalProperties || !(targetProp.flags & SymbolFlags.Optional)) { + if (requireOptionalProperties || !(targetProp.flags & SymbolFlags.Optional || getCheckFlags(targetProp) & CheckFlags.Partial)) { const sourceProp = getPropertyOfType(source, targetProp.escapedName); if (!sourceProp) { yield targetProp; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 23d72875e4..6fba5f149c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3746,19 +3746,21 @@ namespace ts { SyntheticProperty = 1 << 1, // Property in union or intersection type SyntheticMethod = 1 << 2, // Method in union or intersection type Readonly = 1 << 3, // Readonly transient symbol - Partial = 1 << 4, // Synthetic property present in some but not all constituents - HasNonUniformType = 1 << 5, // Synthetic property with non-uniform type in constituents - HasLiteralType = 1 << 6, // Synthetic property with at least one literal type in constituents - ContainsPublic = 1 << 7, // Synthetic property with public constituent(s) - ContainsProtected = 1 << 8, // Synthetic property with protected constituent(s) - ContainsPrivate = 1 << 9, // Synthetic property with private constituent(s) - ContainsStatic = 1 << 10, // Synthetic property with static constituent(s) - Late = 1 << 11, // Late-bound symbol for a computed property with a dynamic name - ReverseMapped = 1 << 12, // Property of reverse-inferred homomorphic mapped type - OptionalParameter = 1 << 13, // Optional parameter - RestParameter = 1 << 14, // Rest parameter + ReadPartial = 1 << 4, // Synthetic property present in some but not all constituents + WritePartial = 1 << 5, // Synthetic property present in some but only satisfied by an index signature in others + HasNonUniformType = 1 << 6, // Synthetic property with non-uniform type in constituents + HasLiteralType = 1 << 7, // Synthetic property with at least one literal type in constituents + ContainsPublic = 1 << 8, // Synthetic property with public constituent(s) + ContainsProtected = 1 << 9, // Synthetic property with protected constituent(s) + ContainsPrivate = 1 << 10, // Synthetic property with private constituent(s) + ContainsStatic = 1 << 11, // Synthetic property with static constituent(s) + Late = 1 << 12, // Late-bound symbol for a computed property with a dynamic name + ReverseMapped = 1 << 13, // Property of reverse-inferred homomorphic mapped type + OptionalParameter = 1 << 14, // Optional parameter + RestParameter = 1 << 15, // Rest parameter Synthetic = SyntheticProperty | SyntheticMethod, - Discriminant = HasNonUniformType | HasLiteralType + Discriminant = HasNonUniformType | HasLiteralType, + Partial = ReadPartial | WritePartial } /* @internal */ @@ -4171,6 +4173,8 @@ namespace ts { } export interface UnionType extends UnionOrIntersectionType { + /* @internal */ + possiblePropertyCache?: SymbolTable; // Cache of _all_ resolved properties less any from aparent members } export interface IntersectionType extends UnionOrIntersectionType { diff --git a/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.errors.txt b/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.errors.txt new file mode 100644 index 0000000000..6a83638ba9 --- /dev/null +++ b/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.errors.txt @@ -0,0 +1,48 @@ +tests/cases/compiler/deepExcessPropertyCheckingWhenTargetIsIntersection.ts(21,33): error TS2322: Type '{ INVALID_PROP_NAME: string; ariaLabel: string; }' is not assignable to type 'ITestProps'. + Object literal may only specify known properties, and 'INVALID_PROP_NAME' does not exist in type 'ITestProps'. +tests/cases/compiler/deepExcessPropertyCheckingWhenTargetIsIntersection.ts(27,34): error TS2326: Types of property 'icon' are incompatible. + Type '{ props: { INVALID_PROP_NAME: string; ariaLabel: string; }; }' is not assignable to type 'NestedProp'. + Types of property 'props' are incompatible. + Type '{ INVALID_PROP_NAME: string; ariaLabel: string; }' is not assignable to type 'ITestProps'. + Object literal may only specify known properties, and 'INVALID_PROP_NAME' does not exist in type 'ITestProps'. + + +==== tests/cases/compiler/deepExcessPropertyCheckingWhenTargetIsIntersection.ts (2 errors) ==== + interface StatelessComponent

{ + (props: P & { children?: number }, context?: any): null; + } + + const TestComponent: StatelessComponent = (props) => { + return null; + } + + interface ITestProps { + ariaLabel?: string; + } + + interface NestedProp { + props: TProps; + } + + interface TestProps { + icon: NestedProp; + } + + TestComponent({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); + ~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ INVALID_PROP_NAME: string; ariaLabel: string; }' is not assignable to type 'ITestProps'. +!!! error TS2322: Object literal may only specify known properties, and 'INVALID_PROP_NAME' does not exist in type 'ITestProps'. +!!! related TS6500 tests/cases/compiler/deepExcessPropertyCheckingWhenTargetIsIntersection.ts:14:3: The expected type comes from property 'props' which is declared here on type 'NestedProp' + + const TestComponent2: StatelessComponent = (props) => { + return null; + } + + TestComponent2({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); + ~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2326: Types of property 'icon' are incompatible. +!!! error TS2326: Type '{ props: { INVALID_PROP_NAME: string; ariaLabel: string; }; }' is not assignable to type 'NestedProp'. +!!! error TS2326: Types of property 'props' are incompatible. +!!! error TS2326: Type '{ INVALID_PROP_NAME: string; ariaLabel: string; }' is not assignable to type 'ITestProps'. +!!! error TS2326: Object literal may only specify known properties, and 'INVALID_PROP_NAME' does not exist in type 'ITestProps'. + \ No newline at end of file diff --git a/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.js b/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.js new file mode 100644 index 0000000000..cb0e6503b7 --- /dev/null +++ b/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.js @@ -0,0 +1,39 @@ +//// [deepExcessPropertyCheckingWhenTargetIsIntersection.ts] +interface StatelessComponent

{ + (props: P & { children?: number }, context?: any): null; +} + +const TestComponent: StatelessComponent = (props) => { + return null; +} + +interface ITestProps { + ariaLabel?: string; +} + +interface NestedProp { + props: TProps; +} + +interface TestProps { + icon: NestedProp; +} + +TestComponent({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); + +const TestComponent2: StatelessComponent = (props) => { + return null; +} + +TestComponent2({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); + + +//// [deepExcessPropertyCheckingWhenTargetIsIntersection.js] +var TestComponent = function (props) { + return null; +}; +TestComponent({ icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } } }); +var TestComponent2 = function (props) { + return null; +}; +TestComponent2({ icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } } }); diff --git a/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.symbols b/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.symbols new file mode 100644 index 0000000000..717cb872ce --- /dev/null +++ b/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.symbols @@ -0,0 +1,71 @@ +=== tests/cases/compiler/deepExcessPropertyCheckingWhenTargetIsIntersection.ts === +interface StatelessComponent

{ +>StatelessComponent : Symbol(StatelessComponent, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 0, 0)) +>P : Symbol(P, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 0, 29)) + + (props: P & { children?: number }, context?: any): null; +>props : Symbol(props, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 1, 3)) +>P : Symbol(P, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 0, 29)) +>children : Symbol(children, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 1, 15)) +>context : Symbol(context, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 1, 36)) +} + +const TestComponent: StatelessComponent = (props) => { +>TestComponent : Symbol(TestComponent, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 4, 5)) +>StatelessComponent : Symbol(StatelessComponent, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 0, 0)) +>TestProps : Symbol(TestProps, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 14, 1)) +>props : Symbol(props, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 4, 54)) + + return null; +} + +interface ITestProps { +>ITestProps : Symbol(ITestProps, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 6, 1)) + + ariaLabel?: string; +>ariaLabel : Symbol(ITestProps.ariaLabel, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 8, 22)) +} + +interface NestedProp { +>NestedProp : Symbol(NestedProp, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 10, 1)) +>TProps : Symbol(TProps, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 12, 21)) + + props: TProps; +>props : Symbol(NestedProp.props, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 12, 30)) +>TProps : Symbol(TProps, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 12, 21)) +} + +interface TestProps { +>TestProps : Symbol(TestProps, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 14, 1)) + + icon: NestedProp; +>icon : Symbol(TestProps.icon, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 16, 21)) +>NestedProp : Symbol(NestedProp, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 10, 1)) +>ITestProps : Symbol(ITestProps, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 6, 1)) +} + +TestComponent({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); +>TestComponent : Symbol(TestComponent, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 4, 5)) +>icon : Symbol(icon, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 20, 15)) +>props : Symbol(props, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 20, 22)) +>INVALID_PROP_NAME : Symbol(INVALID_PROP_NAME, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 20, 31)) +>ariaLabel : Symbol(ariaLabel, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 20, 59)) + +const TestComponent2: StatelessComponent = (props) => { +>TestComponent2 : Symbol(TestComponent2, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 22, 5)) +>StatelessComponent : Symbol(StatelessComponent, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 0, 0)) +>TestProps : Symbol(TestProps, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 14, 1)) +>props2 : Symbol(props2, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 22, 54)) +>x : Symbol(x, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 22, 63)) +>props : Symbol(props, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 22, 79)) + + return null; +} + +TestComponent2({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); +>TestComponent2 : Symbol(TestComponent2, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 22, 5)) +>icon : Symbol(icon, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 26, 16)) +>props : Symbol(props, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 26, 23)) +>INVALID_PROP_NAME : Symbol(INVALID_PROP_NAME, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 26, 32)) +>ariaLabel : Symbol(ariaLabel, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 26, 60)) + diff --git a/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.types b/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.types new file mode 100644 index 0000000000..dc84ff20e0 --- /dev/null +++ b/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.types @@ -0,0 +1,70 @@ +=== tests/cases/compiler/deepExcessPropertyCheckingWhenTargetIsIntersection.ts === +interface StatelessComponent

{ + (props: P & { children?: number }, context?: any): null; +>props : P & { children?: number; } +>children : number +>context : any +>null : null +} + +const TestComponent: StatelessComponent = (props) => { +>TestComponent : StatelessComponent +>(props) => { return null;} : (props: TestProps & { children?: number; }) => any +>props : TestProps & { children?: number; } + + return null; +>null : null +} + +interface ITestProps { + ariaLabel?: string; +>ariaLabel : string +} + +interface NestedProp { + props: TProps; +>props : TProps +} + +interface TestProps { + icon: NestedProp; +>icon : NestedProp +} + +TestComponent({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); +>TestComponent({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}) : null +>TestComponent : StatelessComponent +>{icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }} : { icon: { props: { INVALID_PROP_NAME: string; ariaLabel: string; }; }; } +>icon : { props: { INVALID_PROP_NAME: string; ariaLabel: string; }; } +>{ props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } } : { props: { INVALID_PROP_NAME: string; ariaLabel: string; }; } +>props : { INVALID_PROP_NAME: string; ariaLabel: string; } +>{ INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } : { INVALID_PROP_NAME: string; ariaLabel: string; } +>INVALID_PROP_NAME : string +>'share' : "share" +>ariaLabel : string +>'test label' : "test label" + +const TestComponent2: StatelessComponent = (props) => { +>TestComponent2 : StatelessComponent +>props2 : { x: number; } +>x : number +>(props) => { return null;} : (props: (TestProps & { children?: number; }) | ({ props2: { x: number; }; } & { children?: number; })) => any +>props : (TestProps & { children?: number; }) | ({ props2: { x: number; }; } & { children?: number; }) + + return null; +>null : null +} + +TestComponent2({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); +>TestComponent2({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}) : null +>TestComponent2 : StatelessComponent +>{icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }} : { icon: { props: { INVALID_PROP_NAME: string; ariaLabel: string; }; }; } +>icon : { props: { INVALID_PROP_NAME: string; ariaLabel: string; }; } +>{ props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } } : { props: { INVALID_PROP_NAME: string; ariaLabel: string; }; } +>props : { INVALID_PROP_NAME: string; ariaLabel: string; } +>{ INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } : { INVALID_PROP_NAME: string; ariaLabel: string; } +>INVALID_PROP_NAME : string +>'share' : "share" +>ariaLabel : string +>'test label' : "test label" + diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt b/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt index 5331add9e8..469b6d089b 100644 --- a/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt @@ -20,11 +20,9 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(49,35): error TS2322: Type Object literal may only specify known properties, and 'second' does not exist in type '{ a: 1; b: 1; first: string; }'. tests/cases/compiler/excessPropertyCheckWithUnions.ts(50,35): error TS2322: Type '{ a: 1; b: 1; first: string; third: string; }' is not assignable to type 'Overlapping'. Object literal may only specify known properties, and 'third' does not exist in type '{ a: 1; b: 1; first: string; }'. -tests/cases/compiler/excessPropertyCheckWithUnions.ts(66,9): error TS2322: Type '{ kind: "A"; n: { a: string; b: string; }; }' is not assignable to type 'AB'. - Type '{ kind: "A"; n: { a: string; b: string; }; }' is not assignable to type '{ kind: "A"; n: AN; }'. - Types of property 'n' are incompatible. - Type '{ a: string; b: string; }' is not assignable to type 'AN'. - Object literal may only specify known properties, and 'b' does not exist in type 'AN'. +tests/cases/compiler/excessPropertyCheckWithUnions.ts(66,9): error TS2326: Types of property 'n' are incompatible. + Type '{ a: string; b: string; }' is not assignable to type 'AN'. + Object literal may only specify known properties, and 'b' does not exist in type 'AN'. ==== tests/cases/compiler/excessPropertyCheckWithUnions.ts (10 errors) ==== @@ -127,11 +125,9 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(66,9): error TS2322: Type a: "a", b: "b", // excess -- kind: "A" ~~~~~~ -!!! error TS2322: Type '{ kind: "A"; n: { a: string; b: string; }; }' is not assignable to type 'AB'. -!!! error TS2322: Type '{ kind: "A"; n: { a: string; b: string; }; }' is not assignable to type '{ kind: "A"; n: AN; }'. -!!! error TS2322: Types of property 'n' are incompatible. -!!! error TS2322: Type '{ a: string; b: string; }' is not assignable to type 'AN'. -!!! error TS2322: Object literal may only specify known properties, and 'b' does not exist in type 'AN'. +!!! error TS2326: Types of property 'n' are incompatible. +!!! error TS2326: Type '{ a: string; b: string; }' is not assignable to type 'AN'. +!!! error TS2326: Object literal may only specify known properties, and 'b' does not exist in type 'AN'. } } const abac: AB = { diff --git a/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.errors.txt b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.errors.txt new file mode 100644 index 0000000000..368a71de00 --- /dev/null +++ b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.errors.txt @@ -0,0 +1,116 @@ +tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts(18,19): error TS2322: Type 'number' is not assignable to type 'string'. +tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts(19,31): error TS2322: Type '{ x: string; y: number; }' is not assignable to type 'A'. + Object literal may only specify known properties, and 'y' does not exist in type 'A'. +tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts(22,19): error TS2322: Type 'number' is not assignable to type 'string'. +tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts(23,31): error TS2322: Type '{ x: string; y: number; }' is not assignable to type 'A'. + Object literal may only specify known properties, and 'y' does not exist in type 'A'. +tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts(34,5): error TS2322: Type '{ id: number; url: string; xyz: number; }' is not assignable to type '{ id: number; } & { url: string; }'. + Object literal may only specify known properties, and 'xyz' does not exist in type '{ id: number; } & { url: string; }'. +tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts(43,9): error TS2322: Type '{ id: number; url: string; xyz: number; }' is not assignable to type '{ id: number; } & { url: string; }'. + Object literal may only specify known properties, and 'xyz' does not exist in type '{ id: number; } & { url: string; }'. +tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts(68,32): error TS2322: Type '{ foo: true; bar: true; boo: boolean; }' is not assignable to type 'View'. + Object literal may only specify known properties, and 'boo' does not exist in type 'View'. +tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts(70,50): error TS2322: Type '{ foo: true; bar: true; boo: boolean; }' is not assignable to type 'boolean | View'. + Object literal may only specify known properties, and 'boo' does not exist in type 'View'. + + +==== tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts (8 errors) ==== + // https://github.com/Microsoft/TypeScript/issues/13813 + + interface A { + x: string + } + + interface B { + a: A; + } + + interface C { + c: number; + } + + type D = B & C; + + let a: B = { a: { x: 'hello' } }; // ok + let b: B = { a: { x: 2 } }; // error - types of property x are incompatible + ~ +!!! 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 c: B = { a: { x: 'hello', y: 2 } }; // 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'. +!!! related TS6500 tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts:8:5: The expected type comes from property 'a' which is declared here on type 'B' + + let d: D = { a: { x: 'hello' }, c: 5 }; // ok + let e: D = { a: { x: 2 }, c: 5 }; // error - types of property x are incompatible + ~ +!!! 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 + ~~~~ +!!! 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'. +!!! related TS6500 tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts:8:5: The expected type comes from property 'a' which is declared here on type 'D' + + // https://github.com/Microsoft/TypeScript/issues/18075 + + export type MyType = { id: number; } & { name: string; } & { photo: { id: number; } & { url: string; } } + + export let obj: MyType; + + export const photo: typeof obj.photo = { + id: 1, + url: '', + xyz: 1 // Great! This causes an error! + ~~~~~~ +!!! error TS2322: Type '{ id: number; url: string; xyz: number; }' is not assignable to type '{ id: number; } & { url: string; }'. +!!! error TS2322: Object literal may only specify known properties, and 'xyz' does not exist in type '{ id: number; } & { url: string; }'. + }; + + export const myInstance: MyType = { + id: 1, + name: '', + photo: { + id: 1, + url: '', + xyz: 2 // This should also be an error + ~~~~~~ +!!! error TS2322: Type '{ id: number; url: string; xyz: number; }' is not assignable to type '{ id: number; } & { url: string; }'. +!!! error TS2322: Object literal may only specify known properties, and 'xyz' does not exist in type '{ id: number; } & { url: string; }'. +!!! related TS6500 tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts:27:62: The expected type comes from property 'photo' which is declared here on type 'MyType' + } + }; + + // https://github.com/Microsoft/TypeScript/issues/28616 + + export type View = { [K in keyof T]: T[K] extends object ? boolean | View : boolean }; + + interface TypeC { + foo: string; + bar: string; + } + + interface TypeB { + foo: string, + bar: TypeC + } + + interface TypeA { + foo: string, + bar: TypeB, + } + + let test: View; + + test = { foo: true, bar: true, boo: true } + ~~~~~~~~~ +!!! error TS2322: Type '{ foo: true; bar: true; boo: boolean; }' is not assignable to type 'View'. +!!! error TS2322: Object literal may only specify known properties, and 'boo' does not exist in type 'View'. + + test = { foo: true, bar: { foo: true, bar: true, boo: true } } + ~~~~~~~~~ +!!! error TS2322: Type '{ foo: true; bar: true; boo: boolean; }' is not assignable to type 'boolean | View'. +!!! error TS2322: Object literal may only specify known properties, and 'boo' does not exist in type 'View'. +!!! related TS6500 tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts:63:5: The expected type comes from property 'bar' which is declared here on type 'View' + \ No newline at end of file diff --git a/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.js b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.js new file mode 100644 index 0000000000..f43b7bf83a --- /dev/null +++ b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.js @@ -0,0 +1,100 @@ +//// [excessPropertyChecksWithNestedIntersections.ts] +// https://github.com/Microsoft/TypeScript/issues/13813 + +interface A { + x: string +} + +interface B { + a: A; +} + +interface C { + c: number; +} + +type D = B & C; + +let a: B = { a: { x: 'hello' } }; // ok +let b: B = { a: { x: 2 } }; // error - types of property x are incompatible +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 + +// https://github.com/Microsoft/TypeScript/issues/18075 + +export type MyType = { id: number; } & { name: string; } & { photo: { id: number; } & { url: string; } } + +export let obj: MyType; + +export const photo: typeof obj.photo = { + id: 1, + url: '', + xyz: 1 // Great! This causes an error! +}; + +export const myInstance: MyType = { + id: 1, + name: '', + photo: { + id: 1, + url: '', + xyz: 2 // This should also be an error + } +}; + +// https://github.com/Microsoft/TypeScript/issues/28616 + +export type View = { [K in keyof T]: T[K] extends object ? boolean | View : boolean }; + +interface TypeC { + foo: string; + bar: string; +} + +interface TypeB { + foo: string, + bar: TypeC +} + +interface TypeA { + foo: string, + bar: TypeB, +} + +let test: View; + +test = { foo: true, bar: true, boo: true } + +test = { foo: true, bar: { foo: true, bar: true, boo: true } } + + +//// [excessPropertyChecksWithNestedIntersections.js] +"use strict"; +// https://github.com/Microsoft/TypeScript/issues/13813 +exports.__esModule = true; +var a = { a: { x: 'hello' } }; // ok +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 +exports.photo = { + id: 1, + url: '', + xyz: 1 // Great! This causes an error! +}; +exports.myInstance = { + id: 1, + name: '', + photo: { + id: 1, + url: '', + xyz: 2 // This should also be an error + } +}; +var test; +test = { foo: true, bar: true, boo: true }; +test = { foo: true, bar: { foo: true, bar: true, boo: true } }; diff --git a/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.symbols b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.symbols new file mode 100644 index 0000000000..bf10195045 --- /dev/null +++ b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.symbols @@ -0,0 +1,190 @@ +=== tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts === +// https://github.com/Microsoft/TypeScript/issues/13813 + +interface A { +>A : Symbol(A, Decl(excessPropertyChecksWithNestedIntersections.ts, 0, 0)) + + x: string +>x : Symbol(A.x, Decl(excessPropertyChecksWithNestedIntersections.ts, 2, 13)) +} + +interface B { +>B : Symbol(B, Decl(excessPropertyChecksWithNestedIntersections.ts, 4, 1)) + + a: A; +>a : Symbol(B.a, Decl(excessPropertyChecksWithNestedIntersections.ts, 6, 13)) +>A : Symbol(A, Decl(excessPropertyChecksWithNestedIntersections.ts, 0, 0)) +} + +interface C { +>C : Symbol(C, Decl(excessPropertyChecksWithNestedIntersections.ts, 8, 1)) + + c: number; +>c : Symbol(C.c, Decl(excessPropertyChecksWithNestedIntersections.ts, 10, 13)) +} + +type D = B & C; +>D : Symbol(D, Decl(excessPropertyChecksWithNestedIntersections.ts, 12, 1)) +>B : Symbol(B, Decl(excessPropertyChecksWithNestedIntersections.ts, 4, 1)) +>C : Symbol(C, Decl(excessPropertyChecksWithNestedIntersections.ts, 8, 1)) + +let a: B = { a: { x: 'hello' } }; // ok +>a : Symbol(a, Decl(excessPropertyChecksWithNestedIntersections.ts, 16, 3)) +>B : Symbol(B, Decl(excessPropertyChecksWithNestedIntersections.ts, 4, 1)) +>a : Symbol(a, Decl(excessPropertyChecksWithNestedIntersections.ts, 16, 12)) +>x : Symbol(x, Decl(excessPropertyChecksWithNestedIntersections.ts, 16, 17)) + +let b: B = { a: { x: 2 } }; // error - types of property x are incompatible +>b : Symbol(b, Decl(excessPropertyChecksWithNestedIntersections.ts, 17, 3)) +>B : Symbol(B, Decl(excessPropertyChecksWithNestedIntersections.ts, 4, 1)) +>a : Symbol(a, Decl(excessPropertyChecksWithNestedIntersections.ts, 17, 12)) +>x : Symbol(x, Decl(excessPropertyChecksWithNestedIntersections.ts, 17, 17)) + +let c: B = { a: { x: 'hello', y: 2 } }; // error - y does not exist in type A +>c : Symbol(c, Decl(excessPropertyChecksWithNestedIntersections.ts, 18, 3)) +>B : Symbol(B, Decl(excessPropertyChecksWithNestedIntersections.ts, 4, 1)) +>a : Symbol(a, Decl(excessPropertyChecksWithNestedIntersections.ts, 18, 12)) +>x : Symbol(x, Decl(excessPropertyChecksWithNestedIntersections.ts, 18, 17)) +>y : Symbol(y, Decl(excessPropertyChecksWithNestedIntersections.ts, 18, 29)) + +let d: D = { a: { x: 'hello' }, c: 5 }; // ok +>d : Symbol(d, Decl(excessPropertyChecksWithNestedIntersections.ts, 20, 3)) +>D : Symbol(D, Decl(excessPropertyChecksWithNestedIntersections.ts, 12, 1)) +>a : Symbol(a, Decl(excessPropertyChecksWithNestedIntersections.ts, 20, 12)) +>x : Symbol(x, Decl(excessPropertyChecksWithNestedIntersections.ts, 20, 17)) +>c : Symbol(c, Decl(excessPropertyChecksWithNestedIntersections.ts, 20, 31)) + +let e: D = { a: { x: 2 }, c: 5 }; // error - types of property x are incompatible +>e : Symbol(e, Decl(excessPropertyChecksWithNestedIntersections.ts, 21, 3)) +>D : Symbol(D, Decl(excessPropertyChecksWithNestedIntersections.ts, 12, 1)) +>a : Symbol(a, Decl(excessPropertyChecksWithNestedIntersections.ts, 21, 12)) +>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 +>f : Symbol(f, Decl(excessPropertyChecksWithNestedIntersections.ts, 22, 3)) +>D : Symbol(D, Decl(excessPropertyChecksWithNestedIntersections.ts, 12, 1)) +>a : Symbol(a, Decl(excessPropertyChecksWithNestedIntersections.ts, 22, 12)) +>x : Symbol(x, Decl(excessPropertyChecksWithNestedIntersections.ts, 22, 17)) +>y : Symbol(y, Decl(excessPropertyChecksWithNestedIntersections.ts, 22, 29)) +>c : Symbol(c, Decl(excessPropertyChecksWithNestedIntersections.ts, 22, 37)) + +// https://github.com/Microsoft/TypeScript/issues/18075 + +export type MyType = { id: number; } & { name: string; } & { photo: { id: number; } & { url: string; } } +>MyType : Symbol(MyType, Decl(excessPropertyChecksWithNestedIntersections.ts, 22, 45)) +>id : Symbol(id, Decl(excessPropertyChecksWithNestedIntersections.ts, 26, 22)) +>name : Symbol(name, Decl(excessPropertyChecksWithNestedIntersections.ts, 26, 40)) +>photo : Symbol(photo, Decl(excessPropertyChecksWithNestedIntersections.ts, 26, 60)) +>id : Symbol(id, Decl(excessPropertyChecksWithNestedIntersections.ts, 26, 69)) +>url : Symbol(url, Decl(excessPropertyChecksWithNestedIntersections.ts, 26, 87)) + +export let obj: MyType; +>obj : Symbol(obj, Decl(excessPropertyChecksWithNestedIntersections.ts, 28, 10)) +>MyType : Symbol(MyType, Decl(excessPropertyChecksWithNestedIntersections.ts, 22, 45)) + +export const photo: typeof obj.photo = { +>photo : Symbol(photo, Decl(excessPropertyChecksWithNestedIntersections.ts, 30, 12)) +>obj.photo : Symbol(photo, Decl(excessPropertyChecksWithNestedIntersections.ts, 26, 60)) +>obj : Symbol(obj, Decl(excessPropertyChecksWithNestedIntersections.ts, 28, 10)) +>photo : Symbol(photo, Decl(excessPropertyChecksWithNestedIntersections.ts, 26, 60)) + + id: 1, +>id : Symbol(id, Decl(excessPropertyChecksWithNestedIntersections.ts, 30, 40)) + + url: '', +>url : Symbol(url, Decl(excessPropertyChecksWithNestedIntersections.ts, 31, 10)) + + xyz: 1 // Great! This causes an error! +>xyz : Symbol(xyz, Decl(excessPropertyChecksWithNestedIntersections.ts, 32, 12)) + +}; + +export const myInstance: MyType = { +>myInstance : Symbol(myInstance, Decl(excessPropertyChecksWithNestedIntersections.ts, 36, 12)) +>MyType : Symbol(MyType, Decl(excessPropertyChecksWithNestedIntersections.ts, 22, 45)) + + id: 1, +>id : Symbol(id, Decl(excessPropertyChecksWithNestedIntersections.ts, 36, 35)) + + name: '', +>name : Symbol(name, Decl(excessPropertyChecksWithNestedIntersections.ts, 37, 10)) + + photo: { +>photo : Symbol(photo, Decl(excessPropertyChecksWithNestedIntersections.ts, 38, 13)) + + id: 1, +>id : Symbol(id, Decl(excessPropertyChecksWithNestedIntersections.ts, 39, 12)) + + url: '', +>url : Symbol(url, Decl(excessPropertyChecksWithNestedIntersections.ts, 40, 14)) + + xyz: 2 // This should also be an error +>xyz : Symbol(xyz, Decl(excessPropertyChecksWithNestedIntersections.ts, 41, 16)) + } +}; + +// https://github.com/Microsoft/TypeScript/issues/28616 + +export type View = { [K in keyof T]: T[K] extends object ? boolean | View : boolean }; +>View : Symbol(View, Decl(excessPropertyChecksWithNestedIntersections.ts, 44, 2)) +>T : Symbol(T, Decl(excessPropertyChecksWithNestedIntersections.ts, 48, 17)) +>K : Symbol(K, Decl(excessPropertyChecksWithNestedIntersections.ts, 48, 25)) +>T : Symbol(T, Decl(excessPropertyChecksWithNestedIntersections.ts, 48, 17)) +>T : Symbol(T, Decl(excessPropertyChecksWithNestedIntersections.ts, 48, 17)) +>K : Symbol(K, Decl(excessPropertyChecksWithNestedIntersections.ts, 48, 25)) +>View : Symbol(View, Decl(excessPropertyChecksWithNestedIntersections.ts, 44, 2)) +>T : Symbol(T, Decl(excessPropertyChecksWithNestedIntersections.ts, 48, 17)) +>K : Symbol(K, Decl(excessPropertyChecksWithNestedIntersections.ts, 48, 25)) + +interface TypeC { +>TypeC : Symbol(TypeC, Decl(excessPropertyChecksWithNestedIntersections.ts, 48, 95)) + + foo: string; +>foo : Symbol(TypeC.foo, Decl(excessPropertyChecksWithNestedIntersections.ts, 50, 17)) + + bar: string; +>bar : Symbol(TypeC.bar, Decl(excessPropertyChecksWithNestedIntersections.ts, 51, 16)) +} + +interface TypeB { +>TypeB : Symbol(TypeB, Decl(excessPropertyChecksWithNestedIntersections.ts, 53, 1)) + + foo: string, +>foo : Symbol(TypeB.foo, Decl(excessPropertyChecksWithNestedIntersections.ts, 55, 17)) + + bar: TypeC +>bar : Symbol(TypeB.bar, Decl(excessPropertyChecksWithNestedIntersections.ts, 56, 16)) +>TypeC : Symbol(TypeC, Decl(excessPropertyChecksWithNestedIntersections.ts, 48, 95)) +} + +interface TypeA { +>TypeA : Symbol(TypeA, Decl(excessPropertyChecksWithNestedIntersections.ts, 58, 1)) + + foo: string, +>foo : Symbol(TypeA.foo, Decl(excessPropertyChecksWithNestedIntersections.ts, 60, 17)) + + bar: TypeB, +>bar : Symbol(TypeA.bar, Decl(excessPropertyChecksWithNestedIntersections.ts, 61, 16)) +>TypeB : Symbol(TypeB, Decl(excessPropertyChecksWithNestedIntersections.ts, 53, 1)) +} + +let test: View; +>test : Symbol(test, Decl(excessPropertyChecksWithNestedIntersections.ts, 65, 3)) +>View : Symbol(View, Decl(excessPropertyChecksWithNestedIntersections.ts, 44, 2)) +>TypeA : Symbol(TypeA, Decl(excessPropertyChecksWithNestedIntersections.ts, 58, 1)) + +test = { foo: true, bar: true, boo: true } +>test : Symbol(test, Decl(excessPropertyChecksWithNestedIntersections.ts, 65, 3)) +>foo : Symbol(foo, Decl(excessPropertyChecksWithNestedIntersections.ts, 67, 8)) +>bar : Symbol(bar, Decl(excessPropertyChecksWithNestedIntersections.ts, 67, 19)) +>boo : Symbol(boo, Decl(excessPropertyChecksWithNestedIntersections.ts, 67, 30)) + +test = { foo: true, bar: { foo: true, bar: true, boo: true } } +>test : Symbol(test, Decl(excessPropertyChecksWithNestedIntersections.ts, 65, 3)) +>foo : Symbol(foo, Decl(excessPropertyChecksWithNestedIntersections.ts, 69, 8)) +>bar : Symbol(bar, Decl(excessPropertyChecksWithNestedIntersections.ts, 69, 19)) +>foo : Symbol(foo, Decl(excessPropertyChecksWithNestedIntersections.ts, 69, 26)) +>bar : Symbol(bar, Decl(excessPropertyChecksWithNestedIntersections.ts, 69, 37)) +>boo : Symbol(boo, Decl(excessPropertyChecksWithNestedIntersections.ts, 69, 48)) + diff --git a/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.types b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.types new file mode 100644 index 0000000000..d15b8f7e0d --- /dev/null +++ b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.types @@ -0,0 +1,201 @@ +=== tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts === +// https://github.com/Microsoft/TypeScript/issues/13813 + +interface A { + x: string +>x : string +} + +interface B { + a: A; +>a : A +} + +interface C { + c: number; +>c : number +} + +type D = B & C; +>D : D + +let a: B = { a: { x: 'hello' } }; // ok +>a : B +>{ a: { x: 'hello' } } : { a: { x: string; }; } +>a : { x: string; } +>{ x: 'hello' } : { x: string; } +>x : string +>'hello' : "hello" + +let b: B = { a: { x: 2 } }; // error - types of property x are incompatible +>b : B +>{ a: { x: 2 } } : { a: { x: number; }; } +>a : { x: number; } +>{ x: 2 } : { x: number; } +>x : number +>2 : 2 + +let c: B = { a: { x: 'hello', y: 2 } }; // error - y does not exist in type A +>c : B +>{ a: { x: 'hello', y: 2 } } : { a: { x: string; y: number; }; } +>a : { x: string; y: number; } +>{ x: 'hello', y: 2 } : { x: string; y: number; } +>x : string +>'hello' : "hello" +>y : number +>2 : 2 + +let d: D = { a: { x: 'hello' }, c: 5 }; // ok +>d : D +>{ a: { x: 'hello' }, c: 5 } : { a: { x: string; }; c: number; } +>a : { x: string; } +>{ x: 'hello' } : { x: string; } +>x : string +>'hello' : "hello" +>c : number +>5 : 5 + +let e: D = { a: { x: 2 }, c: 5 }; // error - types of property x are incompatible +>e : D +>{ a: { x: 2 }, c: 5 } : { a: { x: number; }; c: number; } +>a : { x: number; } +>{ x: 2 } : { x: number; } +>x : number +>2 : 2 +>c : number +>5 : 5 + +let f: D = { a: { x: 'hello', y: 2 }, c: 5 }; // should be an error +>f : D +>{ a: { x: 'hello', y: 2 }, c: 5 } : { a: { x: string; y: number; }; c: number; } +>a : { x: string; y: number; } +>{ x: 'hello', y: 2 } : { x: string; y: number; } +>x : string +>'hello' : "hello" +>y : number +>2 : 2 +>c : number +>5 : 5 + +// https://github.com/Microsoft/TypeScript/issues/18075 + +export type MyType = { id: number; } & { name: string; } & { photo: { id: number; } & { url: string; } } +>MyType : MyType +>id : number +>name : string +>photo : { id: number; } & { url: string; } +>id : number +>url : string + +export let obj: MyType; +>obj : MyType + +export const photo: typeof obj.photo = { +>photo : { id: number; } & { url: string; } +>obj.photo : { id: number; } & { url: string; } +>obj : MyType +>photo : { id: number; } & { url: string; } +>{ id: 1, url: '', xyz: 1 // Great! This causes an error!} : { id: number; url: string; xyz: number; } + + id: 1, +>id : number +>1 : 1 + + url: '', +>url : string +>'' : "" + + xyz: 1 // Great! This causes an error! +>xyz : number +>1 : 1 + +}; + +export const myInstance: MyType = { +>myInstance : MyType +>{ id: 1, name: '', photo: { id: 1, url: '', xyz: 2 // This should also be an error }} : { id: number; name: string; photo: { id: number; url: string; xyz: number; }; } + + id: 1, +>id : number +>1 : 1 + + name: '', +>name : string +>'' : "" + + photo: { +>photo : { id: number; url: string; xyz: number; } +>{ id: 1, url: '', xyz: 2 // This should also be an error } : { id: number; url: string; xyz: number; } + + id: 1, +>id : number +>1 : 1 + + url: '', +>url : string +>'' : "" + + xyz: 2 // This should also be an error +>xyz : number +>2 : 2 + } +}; + +// https://github.com/Microsoft/TypeScript/issues/28616 + +export type View = { [K in keyof T]: T[K] extends object ? boolean | View : boolean }; +>View : View + +interface TypeC { + foo: string; +>foo : string + + bar: string; +>bar : string +} + +interface TypeB { + foo: string, +>foo : string + + bar: TypeC +>bar : TypeC +} + +interface TypeA { + foo: string, +>foo : string + + bar: TypeB, +>bar : TypeB +} + +let test: View; +>test : View + +test = { foo: true, bar: true, boo: true } +>test = { foo: true, bar: true, boo: true } : { foo: true; bar: true; boo: boolean; } +>test : View +>{ foo: true, bar: true, boo: true } : { foo: true; bar: true; boo: boolean; } +>foo : true +>true : true +>bar : true +>true : true +>boo : boolean +>true : true + +test = { foo: true, bar: { foo: true, bar: true, boo: true } } +>test = { foo: true, bar: { foo: true, bar: true, boo: true } } : { foo: true; bar: { foo: true; bar: true; boo: boolean; }; } +>test : View +>{ foo: true, bar: { foo: true, bar: true, boo: true } } : { foo: true; bar: { foo: true; bar: true; boo: boolean; }; } +>foo : true +>true : true +>bar : { foo: true; bar: true; boo: boolean; } +>{ foo: true, bar: true, boo: true } : { foo: true; bar: true; boo: boolean; } +>foo : true +>true : true +>bar : true +>true : true +>boo : boolean +>true : true + diff --git a/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.errors.txt b/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.errors.txt new file mode 100644 index 0000000000..4cf8a99e0e --- /dev/null +++ b/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.errors.txt @@ -0,0 +1,44 @@ +tests/cases/compiler/nonObjectUnionNestedExcessPropertyCheck.ts(13,35): error TS2322: Type '{ INVALID_PROP_NAME: string; iconProp: string; }' is not assignable to type 'number | IProps'. + Object literal may only specify known properties, and 'INVALID_PROP_NAME' does not exist in type 'IProps'. +tests/cases/compiler/nonObjectUnionNestedExcessPropertyCheck.ts(16,7): error TS2322: Type '{ nestedProp: { asdfasdf: string; }; iconProp: string; }' is not assignable to type 'number | IProps'. + Type '{ nestedProp: { asdfasdf: string; }; iconProp: string; }' is not assignable to type 'IProps'. + Types of property 'nestedProp' are incompatible. + Type '{ asdfasdf: string; }' has no properties in common with type '{ testBool?: boolean; }'. +tests/cases/compiler/nonObjectUnionNestedExcessPropertyCheck.ts(19,56): error TS2326: Types of property 'nestedProps' are incompatible. + Type '{ INVALID_PROP_NAME: string; iconProp: string; }' is not assignable to type 'IProps'. + Object literal may only specify known properties, and 'INVALID_PROP_NAME' does not exist in type 'IProps'. + + +==== tests/cases/compiler/nonObjectUnionNestedExcessPropertyCheck.ts (3 errors) ==== + interface IProps { + iconProp?: string; + nestedProp?: { + testBool?: boolean; + } + } + + interface INestedProps { + nestedProps?: IProps; + } + + // These are the types of errors we want: + const propB1: IProps | number = { INVALID_PROP_NAME: 'share', iconProp: 'test' }; + ~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ INVALID_PROP_NAME: string; iconProp: string; }' is not assignable to type 'number | IProps'. +!!! error TS2322: Object literal may only specify known properties, and 'INVALID_PROP_NAME' does not exist in type 'IProps'. + + // Nested typing works here and we also get an expected error: + const propB2: IProps | number = { nestedProp: { asdfasdf: 'test' }, iconProp: 'test' }; + ~~~~~~ +!!! error TS2322: Type '{ nestedProp: { asdfasdf: string; }; iconProp: string; }' is not assignable to type 'number | IProps'. +!!! error TS2322: Type '{ nestedProp: { asdfasdf: string; }; iconProp: string; }' is not assignable to type 'IProps'. +!!! error TS2322: Types of property 'nestedProp' are incompatible. +!!! error TS2322: Type '{ asdfasdf: string; }' has no properties in common with type '{ testBool?: boolean; }'. + + // Want an error generated here but there isn't one. + const propA1: INestedProps | number = { nestedProps: { INVALID_PROP_NAME: 'share', iconProp: 'test' } }; + ~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2326: Types of property 'nestedProps' are incompatible. +!!! error TS2326: Type '{ INVALID_PROP_NAME: string; iconProp: string; }' is not assignable to type 'IProps'. +!!! error TS2326: Object literal may only specify known properties, and 'INVALID_PROP_NAME' does not exist in type 'IProps'. + \ No newline at end of file diff --git a/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.js b/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.js new file mode 100644 index 0000000000..b286049e4a --- /dev/null +++ b/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.js @@ -0,0 +1,29 @@ +//// [nonObjectUnionNestedExcessPropertyCheck.ts] +interface IProps { + iconProp?: string; + nestedProp?: { + testBool?: boolean; + } +} + +interface INestedProps { + nestedProps?: IProps; +} + +// These are the types of errors we want: +const propB1: IProps | number = { INVALID_PROP_NAME: 'share', iconProp: 'test' }; + +// Nested typing works here and we also get an expected error: +const propB2: IProps | number = { nestedProp: { asdfasdf: 'test' }, iconProp: 'test' }; + +// Want an error generated here but there isn't one. +const propA1: INestedProps | number = { nestedProps: { INVALID_PROP_NAME: 'share', iconProp: 'test' } }; + + +//// [nonObjectUnionNestedExcessPropertyCheck.js] +// These are the types of errors we want: +var propB1 = { INVALID_PROP_NAME: 'share', iconProp: 'test' }; +// Nested typing works here and we also get an expected error: +var propB2 = { nestedProp: { asdfasdf: 'test' }, iconProp: 'test' }; +// Want an error generated here but there isn't one. +var propA1 = { nestedProps: { INVALID_PROP_NAME: 'share', iconProp: 'test' } }; diff --git a/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.symbols b/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.symbols new file mode 100644 index 0000000000..95e6c33a64 --- /dev/null +++ b/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.symbols @@ -0,0 +1,46 @@ +=== tests/cases/compiler/nonObjectUnionNestedExcessPropertyCheck.ts === +interface IProps { +>IProps : Symbol(IProps, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 0, 0)) + + iconProp?: string; +>iconProp : Symbol(IProps.iconProp, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 0, 18)) + + nestedProp?: { +>nestedProp : Symbol(IProps.nestedProp, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 1, 22)) + + testBool?: boolean; +>testBool : Symbol(testBool, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 2, 18)) + } +} + +interface INestedProps { +>INestedProps : Symbol(INestedProps, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 5, 1)) + + nestedProps?: IProps; +>nestedProps : Symbol(INestedProps.nestedProps, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 7, 24)) +>IProps : Symbol(IProps, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 0, 0)) +} + +// These are the types of errors we want: +const propB1: IProps | number = { INVALID_PROP_NAME: 'share', iconProp: 'test' }; +>propB1 : Symbol(propB1, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 12, 5)) +>IProps : Symbol(IProps, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 0, 0)) +>INVALID_PROP_NAME : Symbol(INVALID_PROP_NAME, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 12, 33)) +>iconProp : Symbol(iconProp, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 12, 61)) + +// Nested typing works here and we also get an expected error: +const propB2: IProps | number = { nestedProp: { asdfasdf: 'test' }, iconProp: 'test' }; +>propB2 : Symbol(propB2, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 15, 5)) +>IProps : Symbol(IProps, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 0, 0)) +>nestedProp : Symbol(nestedProp, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 15, 33)) +>asdfasdf : Symbol(asdfasdf, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 15, 47)) +>iconProp : Symbol(iconProp, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 15, 67)) + +// Want an error generated here but there isn't one. +const propA1: INestedProps | number = { nestedProps: { INVALID_PROP_NAME: 'share', iconProp: 'test' } }; +>propA1 : Symbol(propA1, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 18, 5)) +>INestedProps : Symbol(INestedProps, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 5, 1)) +>nestedProps : Symbol(nestedProps, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 18, 39)) +>INVALID_PROP_NAME : Symbol(INVALID_PROP_NAME, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 18, 54)) +>iconProp : Symbol(iconProp, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 18, 82)) + diff --git a/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.types b/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.types new file mode 100644 index 0000000000..3e05dbd362 --- /dev/null +++ b/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.types @@ -0,0 +1,49 @@ +=== tests/cases/compiler/nonObjectUnionNestedExcessPropertyCheck.ts === +interface IProps { + iconProp?: string; +>iconProp : string + + nestedProp?: { +>nestedProp : { testBool?: boolean; } + + testBool?: boolean; +>testBool : boolean + } +} + +interface INestedProps { + nestedProps?: IProps; +>nestedProps : IProps +} + +// These are the types of errors we want: +const propB1: IProps | number = { INVALID_PROP_NAME: 'share', iconProp: 'test' }; +>propB1 : number | IProps +>{ INVALID_PROP_NAME: 'share', iconProp: 'test' } : { INVALID_PROP_NAME: string; iconProp: string; } +>INVALID_PROP_NAME : string +>'share' : "share" +>iconProp : string +>'test' : "test" + +// Nested typing works here and we also get an expected error: +const propB2: IProps | number = { nestedProp: { asdfasdf: 'test' }, iconProp: 'test' }; +>propB2 : number | IProps +>{ nestedProp: { asdfasdf: 'test' }, iconProp: 'test' } : { nestedProp: { asdfasdf: string; }; iconProp: string; } +>nestedProp : { asdfasdf: string; } +>{ asdfasdf: 'test' } : { asdfasdf: string; } +>asdfasdf : string +>'test' : "test" +>iconProp : string +>'test' : "test" + +// Want an error generated here but there isn't one. +const propA1: INestedProps | number = { nestedProps: { INVALID_PROP_NAME: 'share', iconProp: 'test' } }; +>propA1 : number | INestedProps +>{ nestedProps: { INVALID_PROP_NAME: 'share', iconProp: 'test' } } : { nestedProps: { INVALID_PROP_NAME: string; iconProp: string; }; } +>nestedProps : { INVALID_PROP_NAME: string; iconProp: string; } +>{ INVALID_PROP_NAME: 'share', iconProp: 'test' } : { INVALID_PROP_NAME: string; iconProp: string; } +>INVALID_PROP_NAME : string +>'share' : "share" +>iconProp : string +>'test' : "test" + diff --git a/tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.js b/tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.js new file mode 100644 index 0000000000..fa99607192 --- /dev/null +++ b/tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.js @@ -0,0 +1,17 @@ +//// [unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts] +interface IStringDictionary { + [name: string]: V; +} +interface INumberDictionary { + [idx: number]: V; +} + +declare function forEach(from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T; }, remove: () => void) => any); + +let count = 0; +forEach({ toString: 123 }, () => count++); + + +//// [unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.js] +var count = 0; +forEach({ toString: 123 }, function () { return count++; }); diff --git a/tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.symbols b/tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.symbols new file mode 100644 index 0000000000..f671260a7a --- /dev/null +++ b/tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.symbols @@ -0,0 +1,41 @@ +=== tests/cases/compiler/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts === +interface IStringDictionary { +>IStringDictionary : Symbol(IStringDictionary, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 0, 0)) +>V : Symbol(V, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 0, 28)) + + [name: string]: V; +>name : Symbol(name, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 1, 2)) +>V : Symbol(V, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 0, 28)) +} +interface INumberDictionary { +>INumberDictionary : Symbol(INumberDictionary, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 2, 1)) +>V : Symbol(V, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 3, 28)) + + [idx: number]: V; +>idx : Symbol(idx, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 4, 2)) +>V : Symbol(V, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 3, 28)) +} + +declare function forEach(from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T; }, remove: () => void) => any); +>forEach : Symbol(forEach, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 5, 1)) +>T : Symbol(T, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 25)) +>from : Symbol(from, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 28)) +>IStringDictionary : Symbol(IStringDictionary, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 0, 0)) +>T : Symbol(T, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 25)) +>INumberDictionary : Symbol(INumberDictionary, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 2, 1)) +>T : Symbol(T, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 25)) +>callback : Symbol(callback, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 78)) +>entry : Symbol(entry, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 90)) +>key : Symbol(key, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 98)) +>value : Symbol(value, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 108)) +>T : Symbol(T, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 25)) +>remove : Symbol(remove, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 121)) + +let count = 0; +>count : Symbol(count, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 9, 3)) + +forEach({ toString: 123 }, () => count++); +>forEach : Symbol(forEach, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 5, 1)) +>toString : Symbol(toString, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 10, 9)) +>count : Symbol(count, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 9, 3)) + diff --git a/tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.types b/tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.types new file mode 100644 index 0000000000..83bd83d6ec --- /dev/null +++ b/tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.types @@ -0,0 +1,33 @@ +=== tests/cases/compiler/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts === +interface IStringDictionary { + [name: string]: V; +>name : string +} +interface INumberDictionary { + [idx: number]: V; +>idx : number +} + +declare function forEach(from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T; }, remove: () => void) => any); +>forEach : (from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T; }, remove: () => void) => any) => any +>from : IStringDictionary | INumberDictionary +>callback : (entry: { key: any; value: T; }, remove: () => void) => any +>entry : { key: any; value: T; } +>key : any +>value : T +>remove : () => void + +let count = 0; +>count : number +>0 : 0 + +forEach({ toString: 123 }, () => count++); +>forEach({ toString: 123 }, () => count++) : any +>forEach : (from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T; }, remove: () => void) => any) => any +>{ toString: 123 } : { toString: number; } +>toString : number +>123 : 123 +>() => count++ : () => number +>count++ : number +>count : number + diff --git a/tests/baselines/reference/unionExcessPropsWithPartialMember.js b/tests/baselines/reference/unionExcessPropsWithPartialMember.js new file mode 100644 index 0000000000..cefd480bba --- /dev/null +++ b/tests/baselines/reference/unionExcessPropsWithPartialMember.js @@ -0,0 +1,31 @@ +//// [unionExcessPropsWithPartialMember.ts] +interface A { + unused?: string; + x: string; +} + +interface B { + x: string; + y: string; +} + +declare var ab: A | B; +declare var a: A; + +ab = {...a, y: (null as any as string | undefined)}; // Should be allowed, since `y` is missing on `A` + + +//// [unionExcessPropsWithPartialMember.js] +"use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +ab = __assign({}, a, { y: null }); // Should be allowed, since `y` is missing on `A` diff --git a/tests/baselines/reference/unionExcessPropsWithPartialMember.symbols b/tests/baselines/reference/unionExcessPropsWithPartialMember.symbols new file mode 100644 index 0000000000..a81636816a --- /dev/null +++ b/tests/baselines/reference/unionExcessPropsWithPartialMember.symbols @@ -0,0 +1,35 @@ +=== tests/cases/compiler/unionExcessPropsWithPartialMember.ts === +interface A { +>A : Symbol(A, Decl(unionExcessPropsWithPartialMember.ts, 0, 0)) + + unused?: string; +>unused : Symbol(A.unused, Decl(unionExcessPropsWithPartialMember.ts, 0, 13)) + + x: string; +>x : Symbol(A.x, Decl(unionExcessPropsWithPartialMember.ts, 1, 20)) +} + +interface B { +>B : Symbol(B, Decl(unionExcessPropsWithPartialMember.ts, 3, 1)) + + x: string; +>x : Symbol(B.x, Decl(unionExcessPropsWithPartialMember.ts, 5, 13)) + + y: string; +>y : Symbol(B.y, Decl(unionExcessPropsWithPartialMember.ts, 6, 14)) +} + +declare var ab: A | B; +>ab : Symbol(ab, Decl(unionExcessPropsWithPartialMember.ts, 10, 11)) +>A : Symbol(A, Decl(unionExcessPropsWithPartialMember.ts, 0, 0)) +>B : Symbol(B, Decl(unionExcessPropsWithPartialMember.ts, 3, 1)) + +declare var a: A; +>a : Symbol(a, Decl(unionExcessPropsWithPartialMember.ts, 11, 11)) +>A : Symbol(A, Decl(unionExcessPropsWithPartialMember.ts, 0, 0)) + +ab = {...a, y: (null as any as string | undefined)}; // Should be allowed, since `y` is missing on `A` +>ab : Symbol(ab, Decl(unionExcessPropsWithPartialMember.ts, 10, 11)) +>a : Symbol(a, Decl(unionExcessPropsWithPartialMember.ts, 11, 11)) +>y : Symbol(y, Decl(unionExcessPropsWithPartialMember.ts, 13, 11)) + diff --git a/tests/baselines/reference/unionExcessPropsWithPartialMember.types b/tests/baselines/reference/unionExcessPropsWithPartialMember.types new file mode 100644 index 0000000000..2c3460d977 --- /dev/null +++ b/tests/baselines/reference/unionExcessPropsWithPartialMember.types @@ -0,0 +1,34 @@ +=== tests/cases/compiler/unionExcessPropsWithPartialMember.ts === +interface A { + unused?: string; +>unused : string | undefined + + x: string; +>x : string +} + +interface B { + x: string; +>x : string + + y: string; +>y : string +} + +declare var ab: A | B; +>ab : A | B + +declare var a: A; +>a : A + +ab = {...a, y: (null as any as string | undefined)}; // Should be allowed, since `y` is missing on `A` +>ab = {...a, y: (null as any as string | undefined)} : { y: string | undefined; unused?: string | undefined; x: string; } +>ab : A | B +>{...a, y: (null as any as string | undefined)} : { y: string | undefined; unused?: string | undefined; x: string; } +>a : A +>y : string | undefined +>(null as any as string | undefined) : string | undefined +>null as any as string | undefined : string | undefined +>null as any : any +>null : null + diff --git a/tests/cases/compiler/deepExcessPropertyCheckingWhenTargetIsIntersection.ts b/tests/cases/compiler/deepExcessPropertyCheckingWhenTargetIsIntersection.ts new file mode 100644 index 0000000000..ba7c2cf29d --- /dev/null +++ b/tests/cases/compiler/deepExcessPropertyCheckingWhenTargetIsIntersection.ts @@ -0,0 +1,27 @@ +interface StatelessComponent

{ + (props: P & { children?: number }, context?: any): null; +} + +const TestComponent: StatelessComponent = (props) => { + return null; +} + +interface ITestProps { + ariaLabel?: string; +} + +interface NestedProp { + props: TProps; +} + +interface TestProps { + icon: NestedProp; +} + +TestComponent({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); + +const TestComponent2: StatelessComponent = (props) => { + return null; +} + +TestComponent2({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); diff --git a/tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts b/tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts new file mode 100644 index 0000000000..86bf8f6261 --- /dev/null +++ b/tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts @@ -0,0 +1,70 @@ +// https://github.com/Microsoft/TypeScript/issues/13813 + +interface A { + x: string +} + +interface B { + a: A; +} + +interface C { + c: number; +} + +type D = B & C; + +let a: B = { a: { x: 'hello' } }; // ok +let b: B = { a: { x: 2 } }; // error - types of property x are incompatible +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 + +// https://github.com/Microsoft/TypeScript/issues/18075 + +export type MyType = { id: number; } & { name: string; } & { photo: { id: number; } & { url: string; } } + +export let obj: MyType; + +export const photo: typeof obj.photo = { + id: 1, + url: '', + xyz: 1 // Great! This causes an error! +}; + +export const myInstance: MyType = { + id: 1, + name: '', + photo: { + id: 1, + url: '', + xyz: 2 // This should also be an error + } +}; + +// https://github.com/Microsoft/TypeScript/issues/28616 + +export type View = { [K in keyof T]: T[K] extends object ? boolean | View : boolean }; + +interface TypeC { + foo: string; + bar: string; +} + +interface TypeB { + foo: string, + bar: TypeC +} + +interface TypeA { + foo: string, + bar: TypeB, +} + +let test: View; + +test = { foo: true, bar: true, boo: true } + +test = { foo: true, bar: { foo: true, bar: true, boo: true } } diff --git a/tests/cases/compiler/nonObjectUnionNestedExcessPropertyCheck.ts b/tests/cases/compiler/nonObjectUnionNestedExcessPropertyCheck.ts new file mode 100644 index 0000000000..5050d33c05 --- /dev/null +++ b/tests/cases/compiler/nonObjectUnionNestedExcessPropertyCheck.ts @@ -0,0 +1,19 @@ +interface IProps { + iconProp?: string; + nestedProp?: { + testBool?: boolean; + } +} + +interface INestedProps { + nestedProps?: IProps; +} + +// These are the types of errors we want: +const propB1: IProps | number = { INVALID_PROP_NAME: 'share', iconProp: 'test' }; + +// Nested typing works here and we also get an expected error: +const propB2: IProps | number = { nestedProp: { asdfasdf: 'test' }, iconProp: 'test' }; + +// Want an error generated here but there isn't one. +const propA1: INestedProps | number = { nestedProps: { INVALID_PROP_NAME: 'share', iconProp: 'test' } }; diff --git a/tests/cases/compiler/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts b/tests/cases/compiler/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts new file mode 100644 index 0000000000..757c0f9917 --- /dev/null +++ b/tests/cases/compiler/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts @@ -0,0 +1,11 @@ +interface IStringDictionary { + [name: string]: V; +} +interface INumberDictionary { + [idx: number]: V; +} + +declare function forEach(from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T; }, remove: () => void) => any); + +let count = 0; +forEach({ toString: 123 }, () => count++); diff --git a/tests/cases/compiler/unionExcessPropsWithPartialMember.ts b/tests/cases/compiler/unionExcessPropsWithPartialMember.ts new file mode 100644 index 0000000000..05f0475e0d --- /dev/null +++ b/tests/cases/compiler/unionExcessPropsWithPartialMember.ts @@ -0,0 +1,15 @@ +// @strict: true +interface A { + unused?: string; + x: string; +} + +interface B { + x: string; + y: string; +} + +declare var ab: A | B; +declare var a: A; + +ab = {...a, y: (null as any as string | undefined)}; // Should be allowed, since `y` is missing on `A`