From e6dadfbcaecbda9ba096e7bd8d34c707ff60833e Mon Sep 17 00:00:00 2001 From: Jack Williams Date: Tue, 1 May 2018 02:02:27 +0100 Subject: [PATCH] Initial commit of excess property checking algorithm with tests --- src/compiler/checker.ts | 467 +++++++++++++++--- ...textualTypeWithUnionTypeMembers.errors.txt | 132 +++++ ...lTypeWithUnionTypeObjectLiteral.errors.txt | 8 - .../discriminatedUnionErrorMessage.errors.txt | 12 +- .../excessPropertyCheckWithUnions.errors.txt | 51 +- .../nestedExcessPropertyChecks.errors.txt | 192 +++++++ .../reference/nestedExcessPropertyChecks.js | 143 ++++++ .../nestedExcessPropertyChecks.symbols | 329 ++++++++++++ .../nestedExcessPropertyChecks.types | 419 ++++++++++++++++ .../reference/nestedFreshLiteral.errors.txt | 14 +- ...rConstrainsPropertyDeclarations.errors.txt | 10 +- .../objectLiteralExcessProperties.errors.txt | 18 +- ...pertyAssignmentsInDestructuring.errors.txt | 5 +- ...yAssignmentsInDestructuring_ES6.errors.txt | 5 +- .../compiler/nestedExcessPropertyChecks.ts | 103 ++++ 15 files changed, 1764 insertions(+), 144 deletions(-) create mode 100644 tests/baselines/reference/contextualTypeWithUnionTypeMembers.errors.txt create mode 100644 tests/baselines/reference/nestedExcessPropertyChecks.errors.txt create mode 100644 tests/baselines/reference/nestedExcessPropertyChecks.js create mode 100644 tests/baselines/reference/nestedExcessPropertyChecks.symbols create mode 100644 tests/baselines/reference/nestedExcessPropertyChecks.types create mode 100644 tests/cases/compiler/nestedExcessPropertyChecks.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ae59997b7a2b9..8ee8d075298ba 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -478,6 +478,8 @@ namespace ts { const potentialNewTargetCollisions: Node[] = []; const awaitedTypeStack: number[] = []; + const literalSubPropertyCache: {[id: number]: Symbol[]} = {}; + const diagnostics = createDiagnosticCollection(); // Suggestion diagnostics must have a file. Keyed by source file name. const suggestionDiagnostics = createMultiMap(); @@ -1055,6 +1057,269 @@ namespace ts { // return undefined if we can't find a symbol. } + interface SymbolSet { + [symbolId: number]: Symbol; + } + + interface ExcessSymbolInfo { + excess: SymbolSet; + implicitExcess: SymbolSet; + implicitContained: SymbolSet; + } + + /* + * Get all properties in fresh object literal type (including sub-properties). + */ + function getAllLiteralProperties(type: Type): Symbol[] { + if (literalSubPropertyCache[getTypeId(type)]) { + return literalSubPropertyCache[getTypeId(type)]; + } + if (isObjectLiteralType(type) && type.flags & TypeFlags.FreshLiteral) { + const thisProps = getPropertiesOfObjectType(type); + const mappedProps: ReadonlyArray = thisProps.map(prop => getAllLiteralProperties(getTypeOfPropertyOfType(type, prop.escapedName))); + const result = concatenate(thisProps, flatten(mappedProps)); + literalSubPropertyCache[getTypeId(type)] = result; + return result; + } + if (type.flags & TypeFlags.UnionOrIntersection) { + if ((type).types) { + const allProps: ReadonlyArray = (type).types.map(getAllLiteralProperties); + const result = flatten(allProps); + literalSubPropertyCache[getTypeId(type)] = result; + return result; + } + } + literalSubPropertyCache[getTypeId(type)] = emptyArray; + return emptyArray; + } + + function emptyExcessSymbolInfo(): ExcessSymbolInfo { + return { excess: {}, implicitExcess: {}, implicitContained: {} }; + } + + function symbolSetSize(symbolSet: SymbolSet): number { + let result = 0; + for (const _ in symbolSet) { + result++; + } + return result; + } + + function addSymbols(left: ExcessSymbolInfo, right: ExcessSymbolInfo): ExcessSymbolInfo { + for (const rightID in right.excess) { + left.excess[rightID] = right.excess[rightID]; + } + for (const rightID in right.implicitExcess) { + left.implicitExcess[rightID] = right.implicitExcess[rightID]; + } + for (const rightID in right.implicitContained) { + left.implicitContained[rightID] = right.implicitContained[rightID]; + } + return left; + } + + function mergeSymbolInfoIntersection(left: ExcessSymbolInfo, right: ExcessSymbolInfo): ExcessSymbolInfo { + const newSymbolInfo = emptyExcessSymbolInfo(); + for (const leftID in left.excess) { + if (right.excess[leftID] !== undefined) { + newSymbolInfo.excess[leftID] = left.excess[leftID]; + } + else if (right.implicitExcess[leftID] !== undefined) { + newSymbolInfo.implicitExcess[leftID] = left.excess[leftID]; + } + else if (right.implicitContained[leftID] !== undefined) { + newSymbolInfo.excess[leftID] = left.excess[leftID]; + } + } + for (const leftID in left.implicitExcess) { + if (right.excess[leftID] !== undefined || + right.implicitExcess[leftID] !== undefined || + right.implicitContained[leftID] !== undefined) { + newSymbolInfo.implicitExcess[leftID] = left.implicitExcess[leftID]; + } + } + for (const leftID in left.implicitContained) { + if (right.excess[leftID] !== undefined) { + newSymbolInfo.excess[leftID] = left.implicitContained[leftID]; + } + else if (right.implicitExcess[leftID] !== undefined) { + newSymbolInfo.implicitExcess[leftID] = left.implicitContained[leftID]; + } + else if (right.implicitContained[leftID] !== undefined) { + newSymbolInfo.implicitContained[leftID] = left.implicitContained[leftID]; + } + } + return newSymbolInfo; + } + + function mergeSymbolInfoUnion(left: ExcessSymbolInfo, right: ExcessSymbolInfo): ExcessSymbolInfo { + const newSymbolInfo = emptyExcessSymbolInfo(); + for (const leftID in left.excess) { + if (right.excess[leftID] !== undefined) { + newSymbolInfo.excess[leftID] = left.excess[leftID]; + } + else if (right.implicitExcess[leftID] !== undefined) { + newSymbolInfo.excess[leftID] = left.excess[leftID]; + continue; + } + else if (right.implicitContained[leftID] !== undefined) { + newSymbolInfo.implicitContained[leftID] = left.excess[leftID]; + } + } + for (const leftID in left.implicitExcess) { + if (right.excess[leftID] !== undefined) { + newSymbolInfo.excess[leftID] = left.implicitExcess[leftID]; + } + else if (right.implicitExcess[leftID] !== undefined) { + newSymbolInfo.implicitExcess[leftID] = left.implicitExcess[leftID]; + } + else if (right.implicitContained[leftID] !== undefined) { + newSymbolInfo.implicitContained[leftID] = left.implicitExcess[leftID]; + } + } + for (const leftID in left.implicitContained) { + if (right.excess[leftID] !== undefined || + right.implicitExcess[leftID] !== undefined || + right.implicitContained[leftID] !== undefined) { + newSymbolInfo.implicitContained[leftID] = left.implicitContained[leftID]; + } + } + return newSymbolInfo; + } + + function getExcessProperties( + source: Type, target: Type, inconsistency: (source: Type, target: Type) => void, reportErrors: boolean, + relation: Map, reportExcessPropertyError: (source: Type, target: Type, prop: Symbol) => void + ): Symbol[] { + if (isObjectLiteralType(source) && source.flags & TypeFlags.FreshLiteral && + maybeTypeOfKind(target, TypeFlags.Object) && !(getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) { + const result: Symbol[] = []; + const cancel: [boolean] = [false]; + const { excess } = getExcessPropertiesInSource(source, target, inconsistency, reportErrors, 0, cancel, relation, reportExcessPropertyError); + for (const prop in excess) { + result.push(excess[prop]); + } + return result; + } + return []; + } + + function getExcessPropertiesInSource( + source: Type, target: Type, inconsistency: (source: Type, target: Type) => void, reportErrors: boolean, depth: number, cancel: [boolean], + relation: Map, reportExcessPropertyError: (source: Type, target: Type, prop: Symbol) => void + ): ExcessSymbolInfo { + if (cancel[0]) { + return emptyExcessSymbolInfo(); + } + if (!(isObjectLiteralType(source) && source.flags & TypeFlags.FreshLiteral)) { + return emptyExcessSymbolInfo(); + } + if (target.flags & TypeFlags.Any) { + return emptyExcessSymbolInfo(); + } + const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes); + if ((relation === assignableRelation || relation === definitelyAssignableRelation || relation === comparableRelation) && + (isTypeSubsetOf(globalObjectType, target) || (!isComparingJsxAttributes && isEmptyObjectType(target)))) { + const implicitContained: SymbolSet = {}; + for (const prop of getAllLiteralProperties(source)) { + implicitContained[getSymbolId(prop)] = prop; + } + return { excess: {}, implicitExcess: {}, implicitContained }; + } + if (maybeTypeOfKind(target, TypeFlags.Object) && !(getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) { + if (target.flags & TypeFlags.Union) { + const excessPerBranch: number[] = []; + let symbolInfo: ExcessSymbolInfo; + for (const t of (target).types) { + const branchInfo = getExcessPropertiesInSource(source, t, inconsistency, reportErrors, depth + 1, cancel, relation, reportExcessPropertyError); + excessPerBranch.push(symbolSetSize(branchInfo.excess)); + if (!symbolInfo) { + symbolInfo = branchInfo; + } + else { + symbolInfo = mergeSymbolInfoUnion(symbolInfo, branchInfo); + } + } + if (!symbolInfo) { + return emptyExcessSymbolInfo(); + } + const lookForDoubleDip = excessPerBranch.every(n => n > symbolSetSize(symbolInfo.excess)); + if (lookForDoubleDip) { + inconsistency(source, target); + } + if (reportErrors && depth === 0) { + for (const id in symbolInfo.excess) { + reportExcessPropertyError(source, target, symbolInfo.excess[id]); + cancel[0] = true; + break; + } + } + return symbolInfo; + } + if (target.flags & TypeFlags.Intersection) { + let symbolInfo: ExcessSymbolInfo; + for (const t of (target).types) { + const branchInfo = getExcessPropertiesInSource(source, t, inconsistency, reportErrors, depth + 1, cancel, relation, reportExcessPropertyError); + if (!symbolInfo) { + symbolInfo = branchInfo; + } + else { + symbolInfo = mergeSymbolInfoIntersection(symbolInfo, branchInfo); + } + } + if (!symbolInfo) { + return emptyExcessSymbolInfo(); + } + if (reportErrors && depth === 0) { + for (const id in symbolInfo.excess) { + reportExcessPropertyError(source, target, symbolInfo.excess[id]); + cancel[0] = true; + break; + } + } + return symbolInfo; + } + if (target.flags & TypeFlags.Object) { + let symbolInfo: ExcessSymbolInfo = emptyExcessSymbolInfo(); + let excess: Symbol[] = []; + for (const prop of getPropertiesOfObjectType(source)) { + const sourcePropType = getTypeOfPropertyOfType(source, prop.escapedName); + if (isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) { + // recurse onto object property + const targetPropType = getTypeOfPropertyOfType(target, prop.escapedName); + if (targetPropType) { + const excessSubProperties = + getExcessPropertiesInSource(sourcePropType, targetPropType, inconsistency, reportErrors, depth + 1, cancel, relation, reportExcessPropertyError); + symbolInfo = addSymbols(symbolInfo, excessSubProperties); + } + } + else { + // add this property as excess, and any sub properties. + excess.push(prop); + excess = concatenate(excess, getAllLiteralProperties(sourcePropType)); + } + } + for (const prop of excess) { + const id = getSymbolId(prop); + symbolInfo.excess[id] = prop; + } + if (reportErrors && depth === 0) { + for (const id in symbolInfo.excess) { + reportExcessPropertyError(source, target, symbolInfo.excess[id]); + cancel[0] = true; + break; + } + } + return symbolInfo; + } + } + const implicitExcess: SymbolSet = {}; + for (const prop of getAllLiteralProperties(source)) { + implicitExcess[getSymbolId(prop)] = prop; + } + return { excess: {}, implicitExcess, implicitContained: {} }; + } + /** * Get symbols that represent parameter-property-declaration as parameter and as property declaration * @param parameter a parameterDeclaration node @@ -10116,24 +10381,24 @@ namespace ts { } } - 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; - } + // 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 @@ -10172,20 +10437,40 @@ namespace ts { isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True; if (isObjectLiteralType(source) && source.flags & TypeFlags.FreshLiteral) { - const discriminantType = target.flags & TypeFlags.Union ? findMatchingDiscriminantType(source, target as UnionType) : undefined; - if (hasExcessProperties(source, target, discriminantType, reportErrors)) { +// const discriminantType = target.flags & TypeFlags.Union ? findMatchingDiscriminantType(source, target as UnionType) : undefined; + let inconsistencyFound = false; + let doubleDipSource: Type; + let doubleDipTarget: Type; + const inconsistency = (source: Type, target: Type) => { + doubleDipSource = source; + doubleDipTarget = target; + inconsistencyFound = true; + }; + const excessSymbols = getExcessProperties(source, target, inconsistency, reportErrors, relation, reportExcessPropertyError); + source = getRegularTypeOfObjectLiteral(source); + if (inconsistencyFound || excessSymbols.length > 0) { + if (reportError && errorNode && inconsistencyFound && doubleDipSource !== source && doubleDipTarget !== target) { + reportRelationError(headMessage, doubleDipSource, doubleDipTarget); + } if (reportErrors) { reportRelationError(headMessage, source, target); } return Ternary.False; } + // if (hasExcessProperties(source, target, discriminantType, reportErrors)) { + // if (reportErrors) { + // reportRelationError(headMessage, source, target); + // } + // 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 (isUnionOrIntersectionTypeWithoutNullableConstituents(target) && !discriminantType) { + // source = getRegularTypeOfObjectLiteral(source); + // } } if (relation !== comparableRelation && @@ -10327,60 +10612,96 @@ namespace ts { return Ternary.False; } - function hasExcessProperties(source: FreshObjectLiteralType, target: Type, discriminant: Type | undefined, reportErrors: boolean): boolean { - if (maybeTypeOfKind(target, TypeFlags.Object) && !(getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) { - const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes); - if ((relation === assignableRelation || relation === definitelyAssignableRelation || relation === comparableRelation) && - (isTypeSubsetOf(globalObjectType, target) || (!isComparingJsxAttributes && isEmptyObjectType(target)))) { - return false; - } - if (discriminant) { - // check excess properties against discriminant type only, not the entire union - return hasExcessProperties(source, discriminant, /*discriminant*/ undefined, reportErrors); - } - for (const prop of getPropertiesOfObjectType(source)) { - if (!isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) { - if (reportErrors) { - // We know *exactly* where things went wrong when comparing the types. - // Use this property as the error node as this will be more helpful in - // reasoning about what went wrong. - Debug.assert(!!errorNode); - if (isJsxAttributes(errorNode) || isJsxOpeningLikeElement(errorNode)) { - // JsxAttributes has an object-literal flag and undergo same type-assignablity check as normal object-literal. - // However, using an object-literal error message will be very confusing to the users so we give different a message. - reportError(Diagnostics.Property_0_does_not_exist_on_type_1, symbolToString(prop), typeToString(target)); - } - else { - // use the property's value declaration if the property is assigned inside the literal itself - const objectLiteralDeclaration = source.symbol && firstOrUndefined(source.symbol.declarations); - let suggestion; - if (prop.valueDeclaration && findAncestor(prop.valueDeclaration, d => d === objectLiteralDeclaration)) { - const propDeclaration = prop.valueDeclaration as ObjectLiteralElementLike; - Debug.assertNode(propDeclaration, isObjectLiteralElementLike); - - errorNode = propDeclaration; + function reportExcessPropertyError(source: Type, target: Type, prop: Symbol): void { + // We know *exactly* where things went wrong when comparing the types. + // Use this property as the error node as this will be more helpful in + // reasoning about what went wrong. + Debug.assert(!!errorNode); + if (isJsxAttributes(errorNode) || isJsxOpeningLikeElement(errorNode)) { + // JsxAttributes has an object-literal flag and undergo same type-assignablity check as normal object-literal. + // However, using an object-literal error message will be very confusing to the users so we give different a message. + reportError(Diagnostics.Property_0_does_not_exist_on_type_1, symbolToString(prop), typeToString(target)); + } + else { + // use the property's value declaration if the property is assigned inside the literal itself + const objectLiteralDeclaration = source.symbol && firstOrUndefined(source.symbol.declarations); + let suggestion; + if (prop.valueDeclaration && findAncestor(prop.valueDeclaration, d => d === objectLiteralDeclaration)) { + const propDeclaration = prop.valueDeclaration as ObjectLiteralElementLike; + Debug.assertNode(propDeclaration, isObjectLiteralElementLike); - if (isIdentifier(propDeclaration.name)) { - suggestion = getSuggestionForNonexistentProperty(propDeclaration.name, target); - } - } + errorNode = propDeclaration; - if (suggestion !== undefined) { - reportError(Diagnostics.Object_literal_may_only_specify_known_properties_but_0_does_not_exist_in_type_1_Did_you_mean_to_write_2, - symbolToString(prop), typeToString(target), suggestion); - } - else { - reportError(Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, - symbolToString(prop), typeToString(target)); - } - } - } - return true; + if (isIdentifier(propDeclaration.name)) { + suggestion = getSuggestionForNonexistentProperty(propDeclaration.name, target); } } - } - return false; - } + + if (suggestion !== undefined) { + reportError(Diagnostics.Object_literal_may_only_specify_known_properties_but_0_does_not_exist_in_type_1_Did_you_mean_to_write_2, + symbolToString(prop), typeToString(target), suggestion); + } + else { + reportError(Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, + symbolToString(prop), typeToString(target)); + } + } + } + + // function hasExcessProperties(source: FreshObjectLiteralType, target: Type, discriminant: Type | undefined, reportErrors: boolean): boolean { + // if (maybeTypeOfKind(target, TypeFlags.Object) && !(getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) { + // const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes); + // if ((relation === assignableRelation || relation === definitelyAssignableRelation || relation === comparableRelation) && + // (isTypeSubsetOf(globalObjectType, target) || (!isComparingJsxAttributes && isEmptyObjectType(target)))) { + // return false; + // } + // if (discriminant) { + // // check excess properties against discriminant type only, not the entire union + // return hasExcessProperties(source, discriminant, /*discriminant*/ undefined, reportErrors); + // } + // for (const prop of getPropertiesOfObjectType(source)) { + // if (!isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) { + // if (reportErrors) { + // // We know *exactly* where things went wrong when comparing the types. + // // Use this property as the error node as this will be more helpful in + // // reasoning about what went wrong. + // Debug.assert(!!errorNode); + // if (isJsxAttributes(errorNode) || isJsxOpeningLikeElement(errorNode)) { + // // JsxAttributes has an object-literal flag and undergo same type-assignablity check as normal object-literal. + // // However, using an object-literal error message will be very confusing to the users so we give different a message. + // reportError(Diagnostics.Property_0_does_not_exist_on_type_1, symbolToString(prop), typeToString(target)); + // } + // else { + // // use the property's value declaration if the property is assigned inside the literal itself + // const objectLiteralDeclaration = source.symbol && firstOrUndefined(source.symbol.declarations); + // let suggestion; + // if (prop.valueDeclaration && findAncestor(prop.valueDeclaration, d => d === objectLiteralDeclaration)) { + // const propDeclaration = prop.valueDeclaration as ObjectLiteralElementLike; + // Debug.assertNode(propDeclaration, isObjectLiteralElementLike); + + // errorNode = propDeclaration; + + // if (isIdentifier(propDeclaration.name)) { + // suggestion = getSuggestionForNonexistentProperty(propDeclaration.name, target); + // } + // } + + // if (suggestion !== undefined) { + // reportError(Diagnostics.Object_literal_may_only_specify_known_properties_but_0_does_not_exist_in_type_1_Did_you_mean_to_write_2, + // symbolToString(prop), typeToString(target), suggestion); + // } + // else { + // reportError(Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, + // symbolToString(prop), typeToString(target)); + // } + // } + // } + // return true; + // } + // } + // } + // return false; + // } function eachTypeRelatedToSomeType(source: UnionOrIntersectionType, target: UnionOrIntersectionType): Ternary { let result = Ternary.True; diff --git a/tests/baselines/reference/contextualTypeWithUnionTypeMembers.errors.txt b/tests/baselines/reference/contextualTypeWithUnionTypeMembers.errors.txt new file mode 100644 index 0000000000000..5622e52f02a91 --- /dev/null +++ b/tests/baselines/reference/contextualTypeWithUnionTypeMembers.errors.txt @@ -0,0 +1,132 @@ +tests/cases/conformance/types/union/contextualTypeWithUnionTypeMembers.ts(42,5): error TS2322: Type '{ commonPropertyType: string; commonMethodType: (a: string) => string; commonMethodWithTypeParameter: (a: number) => number; methodOnlyInI1: (a: string) => string; propertyOnlyInI1: string; methodOnlyInI2: (a: string) => string; propertyOnlyInI2: string; }' is not assignable to type 'I1 | I2'. +tests/cases/conformance/types/union/contextualTypeWithUnionTypeMembers.ts(52,5): error TS2322: Type '(I1 | I2 | { commonPropertyType: string; commonMethodType: (a: string) => string; commonMethodWithTypeParameter: (a: number) => number; methodOnlyInI1: (a: string) => string; propertyOnlyInI1: string; methodOnlyInI2: (a: string) => string; propertyOnlyInI2: string; })[]' is not assignable to type '(I1 | I2)[]'. + Type 'I1 | I2 | { commonPropertyType: string; commonMethodType: (a: string) => string; commonMethodWithTypeParameter: (a: number) => number; methodOnlyInI1: (a: string) => string; propertyOnlyInI1: string; methodOnlyInI2: (a: string) => string; propertyOnlyInI2: string; }' is not assignable to type 'I1 | I2'. + Type '{ commonPropertyType: string; commonMethodType: (a: string) => string; commonMethodWithTypeParameter: (a: number) => number; methodOnlyInI1: (a: string) => string; propertyOnlyInI1: string; methodOnlyInI2: (a: string) => string; propertyOnlyInI2: string; }' is not assignable to type 'I1 | I2'. + + +==== tests/cases/conformance/types/union/contextualTypeWithUnionTypeMembers.ts (2 errors) ==== + //When used as a contextual type, a union type U has those members that are present in any of + // its constituent types, with types that are unions of the respective members in the constituent types. + interface I1 { + commonMethodType(a: string): string; + commonPropertyType: string; + commonMethodWithTypeParameter(a: T): T; + + methodOnlyInI1(a: string): string; + propertyOnlyInI1: string; + } + interface I2 { + commonMethodType(a: string): string; + commonPropertyType: string; + commonMethodWithTypeParameter(a: T): T; + + methodOnlyInI2(a: string): string; + propertyOnlyInI2: string; + } + + // Let S be the set of types in U that has a property P. + // If S is not empty, U has a property P of a union type of the types of P from each type in S. + var i1: I1; + var i2: I2; + var i1Ori2: I1 | I2 = i1; + var i1Ori2: I1 | I2 = i2; + var i1Ori2: I1 | I2 = { // Like i1 + commonPropertyType: "hello", + commonMethodType: a=> a, + commonMethodWithTypeParameter: a => a, + + methodOnlyInI1: a => a, + propertyOnlyInI1: "Hello", + }; + var i1Ori2: I1 | I2 = { // Like i2 + commonPropertyType: "hello", + commonMethodType: a=> a, + commonMethodWithTypeParameter: a => a, + + methodOnlyInI2: a => a, + propertyOnlyInI2: "Hello", + }; + var i1Ori2: I1 | I2 = { // Like i1 and i2 both + ~~~~~~ +!!! error TS2322: Type '{ commonPropertyType: string; commonMethodType: (a: string) => string; commonMethodWithTypeParameter: (a: number) => number; methodOnlyInI1: (a: string) => string; propertyOnlyInI1: string; methodOnlyInI2: (a: string) => string; propertyOnlyInI2: string; }' is not assignable to type 'I1 | I2'. + commonPropertyType: "hello", + commonMethodType: a=> a, + commonMethodWithTypeParameter: a => a, + methodOnlyInI1: a => a, + propertyOnlyInI1: "Hello", + methodOnlyInI2: a => a, + propertyOnlyInI2: "Hello", + }; + + var arrayI1OrI2: Array | I2> = [i1, i2, { // Like i1 + ~~~~~~~~~~~ +!!! error TS2322: Type '(I1 | I2 | { commonPropertyType: string; commonMethodType: (a: string) => string; commonMethodWithTypeParameter: (a: number) => number; methodOnlyInI1: (a: string) => string; propertyOnlyInI1: string; methodOnlyInI2: (a: string) => string; propertyOnlyInI2: string; })[]' is not assignable to type '(I1 | I2)[]'. +!!! error TS2322: Type 'I1 | I2 | { commonPropertyType: string; commonMethodType: (a: string) => string; commonMethodWithTypeParameter: (a: number) => number; methodOnlyInI1: (a: string) => string; propertyOnlyInI1: string; methodOnlyInI2: (a: string) => string; propertyOnlyInI2: string; }' is not assignable to type 'I1 | I2'. +!!! error TS2322: Type '{ commonPropertyType: string; commonMethodType: (a: string) => string; commonMethodWithTypeParameter: (a: number) => number; methodOnlyInI1: (a: string) => string; propertyOnlyInI1: string; methodOnlyInI2: (a: string) => string; propertyOnlyInI2: string; }' is not assignable to type 'I1 | I2'. + commonPropertyType: "hello", + commonMethodType: a=> a, + commonMethodWithTypeParameter: a => a, + + methodOnlyInI1: a => a, + propertyOnlyInI1: "Hello", + }, + { // Like i2 + commonPropertyType: "hello", + commonMethodType: a=> a, + commonMethodWithTypeParameter: a => a, + + methodOnlyInI2: a => a, + propertyOnlyInI2: "Hello", + }, { // Like i1 and i2 both + commonPropertyType: "hello", + commonMethodType: a=> a, + commonMethodWithTypeParameter: a => a, + methodOnlyInI1: a => a, + propertyOnlyInI1: "Hello", + methodOnlyInI2: a => a, + propertyOnlyInI2: "Hello", + }]; + + interface I11 { + commonMethodDifferentReturnType(a: string, b: number): string; + commonPropertyDifferentType: string; + } + interface I21 { + commonMethodDifferentReturnType(a: string, b: number): number; + commonPropertyDifferentType: number; + } + var i11: I11; + var i21: I21; + var i11Ori21: I11 | I21 = i11; + var i11Ori21: I11 | I21 = i21; + var i11Ori21: I11 | I21 = { + // Like i1 + commonMethodDifferentReturnType: (a, b) => { + var z = a.charAt(b); + return z; + }, + commonPropertyDifferentType: "hello", + }; + var i11Ori21: I11 | I21 = { + // Like i2 + commonMethodDifferentReturnType: (a, b) => { + var z = a.charCodeAt(b); + return z; + }, + commonPropertyDifferentType: 10, + }; + var arrayOrI11OrI21: Array = [i11, i21, i11 || i21, { + // Like i1 + commonMethodDifferentReturnType: (a, b) => { + var z = a.charAt(b); + return z; + }, + commonPropertyDifferentType: "hello", + }, { + // Like i2 + commonMethodDifferentReturnType: (a, b) => { + var z = a.charCodeAt(b); + return z; + }, + commonPropertyDifferentType: 10, + }]; \ No newline at end of file diff --git a/tests/baselines/reference/contextualTypeWithUnionTypeObjectLiteral.errors.txt b/tests/baselines/reference/contextualTypeWithUnionTypeObjectLiteral.errors.txt index 651d4f5381f4d..950689a2a50f5 100644 --- a/tests/baselines/reference/contextualTypeWithUnionTypeObjectLiteral.errors.txt +++ b/tests/baselines/reference/contextualTypeWithUnionTypeObjectLiteral.errors.txt @@ -17,10 +17,6 @@ tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts( Type '{ prop: string | number; anotherP: string; }' is not assignable to type '{ prop: number; anotherP1: number; }'. Property 'anotherP1' is missing in type '{ prop: string | number; anotherP: string; }'. tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(29,5): error TS2322: Type '{ prop: string | number; anotherP: string; anotherP1: number; }' is not assignable to type '{ prop: string; anotherP: string; } | { prop: number; anotherP1: number; }'. - Type '{ prop: string | number; anotherP: string; anotherP1: number; }' is not assignable to type '{ prop: number; anotherP1: number; }'. - Types of property 'prop' are incompatible. - Type 'string | number' is not assignable to type 'number'. - Type 'string' is not assignable to type 'number'. tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(57,5): error TS2322: Type '{ commonMethodDifferentReturnType: (a: string, b: number) => string | number; }' is not assignable to type 'I11 | I21'. Type '{ commonMethodDifferentReturnType: (a: string, b: number) => string | number; }' is not assignable to type 'I21'. Types of property 'commonMethodDifferentReturnType' are incompatible. @@ -83,10 +79,6 @@ tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts( var objStrOrNum8: { prop: string; anotherP: string; } | { prop: number; anotherP1: number } = { ~~~~~~~~~~~~ !!! error TS2322: Type '{ prop: string | number; anotherP: string; anotherP1: number; }' is not assignable to type '{ prop: string; anotherP: string; } | { prop: number; anotherP1: number; }'. -!!! error TS2322: Type '{ prop: string | number; anotherP: string; anotherP1: number; }' is not assignable to type '{ prop: number; anotherP1: number; }'. -!!! error TS2322: Types of property 'prop' are incompatible. -!!! error TS2322: Type 'string | number' is not assignable to type 'number'. -!!! error TS2322: Type 'string' is not assignable to type 'number'. prop: strOrNumber, anotherP: str, anotherP1: num diff --git a/tests/baselines/reference/discriminatedUnionErrorMessage.errors.txt b/tests/baselines/reference/discriminatedUnionErrorMessage.errors.txt index e54727befd0e1..6f1eb511d3714 100644 --- a/tests/baselines/reference/discriminatedUnionErrorMessage.errors.txt +++ b/tests/baselines/reference/discriminatedUnionErrorMessage.errors.txt @@ -1,5 +1,6 @@ -tests/cases/compiler/discriminatedUnionErrorMessage.ts(10,5): error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Shape'. - Object literal may only specify known properties, and 'x' does not exist in type 'Square'. +tests/cases/compiler/discriminatedUnionErrorMessage.ts(8,5): error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Shape'. + Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Square'. + Property 'size' is missing in type '{ kind: "sq"; x: number; y: number; }'. ==== tests/cases/compiler/discriminatedUnionErrorMessage.ts (1 errors) ==== @@ -11,11 +12,12 @@ tests/cases/compiler/discriminatedUnionErrorMessage.ts(10,5): error TS2322: Type | Rectangle | Circle; let shape: Shape = { - kind: "sq", - x: 12, ~~~~~ !!! error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Shape'. -!!! error TS2322: Object literal may only specify known properties, and 'x' does not exist in type 'Square'. +!!! error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Square'. +!!! error TS2322: Property 'size' is missing in type '{ kind: "sq"; x: number; y: number; }'. + kind: "sq", + x: 12, y: 13, } \ No newline at end of file diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt b/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt index 958bf059324f1..11fbc3327e415 100644 --- a/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt @@ -1,10 +1,10 @@ -tests/cases/compiler/excessPropertyCheckWithUnions.ts(10,30): error TS2322: Type '{ tag: "T"; a1: string; }' is not assignable to type 'ADT'. - Object literal may only specify known properties, and 'a1' does not exist in type '{ tag: "T"; }'. -tests/cases/compiler/excessPropertyCheckWithUnions.ts(11,21): error TS2322: Type '{ tag: "A"; d20: number; }' is not assignable to type 'ADT'. - Object literal may only specify known properties, and 'd20' does not exist in type '{ tag: "A"; a1: string; }'. +tests/cases/compiler/excessPropertyCheckWithUnions.ts(11,1): error TS2322: Type '{ tag: "A"; d20: number; }' is not assignable to type 'ADT'. + Type '{ tag: "A"; d20: number; }' is not assignable to type '{ tag: "A"; a1: string; }'. + Property 'a1' is missing in type '{ tag: "A"; d20: number; }'. tests/cases/compiler/excessPropertyCheckWithUnions.ts(12,1): error TS2322: Type '{ tag: "D"; }' is not assignable to type 'ADT'. Type '{ tag: "D"; }' is not assignable to type '{ tag: "D"; d20: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20; }'. Property 'd20' is missing in type '{ tag: "D"; }'. +tests/cases/compiler/excessPropertyCheckWithUnions.ts(30,1): error TS2322: Type '{ tag: "A"; x: string; y: number; }' is not assignable to type 'Ambiguous'. tests/cases/compiler/excessPropertyCheckWithUnions.ts(33,28): error TS2322: Type '{ tag: "A"; x: string; extra: number; }' is not assignable to type 'Ambiguous'. Object literal may only specify known properties, and 'extra' does not exist in type 'Ambiguous'. tests/cases/compiler/excessPropertyCheckWithUnions.ts(34,26): error TS2322: Type '{ tag: "A"; y: number; extra: number; }' is not assignable to type 'Ambiguous'. @@ -17,18 +17,14 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(40,1): error TS2322: Type Type '{ tag: "A"; z: true; }' is not assignable to type '{ tag: "C"; }'. Types of property 'tag' are incompatible. Type '"A"' is not assignable to type '"C"'. -tests/cases/compiler/excessPropertyCheckWithUnions.ts(49,35): error TS2322: Type '{ a: 1; b: 1; first: string; second: string; }' is not assignable to type 'Overlapping'. - 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(49,1): error TS2322: Type '{ a: 1; b: 1; first: string; second: string; }' is not assignable to type 'Overlapping'. +tests/cases/compiler/excessPropertyCheckWithUnions.ts(50,1): error TS2322: Type '{ a: 1; b: 1; first: string; third: string; }' is not assignable to type 'Overlapping'. +tests/cases/compiler/excessPropertyCheckWithUnions.ts(62,7): error TS2322: Type '{ kind: "A"; n: { a: string; b: string; }; }' is not assignable to type 'AB'. +tests/cases/compiler/excessPropertyCheckWithUnions.ts(69,7): error TS2322: Type '{ kind: "A"; n: { a: string; c: string; }; }' is not assignable to type 'AB'. + Type '{ a: string; c: string; }' is not assignable to type 'AN'. -==== tests/cases/compiler/excessPropertyCheckWithUnions.ts (10 errors) ==== +==== tests/cases/compiler/excessPropertyCheckWithUnions.ts (11 errors) ==== type ADT = { tag: "A", a1: string @@ -39,13 +35,11 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(66,9): error TS2322: Type tag: "T", } let wrong: ADT = { tag: "T", a1: "extra" } - ~~~~~~~~~~~ -!!! error TS2322: Type '{ tag: "T"; a1: string; }' is not assignable to type 'ADT'. -!!! error TS2322: Object literal may only specify known properties, and 'a1' does not exist in type '{ tag: "T"; }'. wrong = { tag: "A", d20: 12 } - ~~~~~~~ + ~~~~~ !!! error TS2322: Type '{ tag: "A"; d20: number; }' is not assignable to type 'ADT'. -!!! error TS2322: Object literal may only specify known properties, and 'd20' does not exist in type '{ tag: "A"; a1: string; }'. +!!! error TS2322: Type '{ tag: "A"; d20: number; }' is not assignable to type '{ tag: "A"; a1: string; }'. +!!! error TS2322: Property 'a1' is missing in type '{ tag: "A"; d20: number; }'. wrong = { tag: "D" } ~~~~~ !!! error TS2322: Type '{ tag: "D"; }' is not assignable to type 'ADT'. @@ -69,6 +63,8 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(66,9): error TS2322: Type amb = { tag: "A", x: "hi" } amb = { tag: "A", y: 12 } amb = { tag: "A", x: "hi", y: 12 } + ~~~ +!!! error TS2322: Type '{ tag: "A"; x: string; y: number; }' is not assignable to type 'Ambiguous'. // correctly error on excess property 'extra', even when ambiguous amb = { tag: "A", x: "hi", extra: 12 } @@ -104,13 +100,11 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(66,9): error TS2322: Type // these two are still errors despite their doubled up discriminants over = { a: 1, b: 1, first: "ok", second: "error" } - ~~~~~~~~~~~~~~~ + ~~~~ !!! error TS2322: Type '{ a: 1; b: 1; first: string; second: string; }' is not assignable to type 'Overlapping'. -!!! error TS2322: Object literal may only specify known properties, and 'second' does not exist in type '{ a: 1; b: 1; first: string; }'. over = { a: 1, b: 1, first: "ok", third: "error" } - ~~~~~~~~~~~~~~ + ~~~~ !!! error TS2322: Type '{ a: 1; b: 1; first: string; third: string; }' is not assignable to type 'Overlapping'. -!!! error TS2322: Object literal may only specify known properties, and 'third' does not exist in type '{ a: 1; b: 1; first: string; }'. // Freshness disappears after spreading a union declare let t0: { a: any, b: any } | { d: any, e: any } @@ -123,19 +117,18 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(66,9): error TS2322: Type type BN = { b: string } type AB = { kind: "A", n: AN } | { kind: "B", n: BN } const abab: AB = { + ~~~~ +!!! error TS2322: Type '{ kind: "A"; n: { a: string; b: string; }; }' is not assignable to type 'AB'. kind: "A", n: { 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'. } } const abac: AB = { + ~~~~ +!!! error TS2322: Type '{ kind: "A"; n: { a: string; c: string; }; }' is not assignable to type 'AB'. +!!! error TS2322: Type '{ a: string; c: string; }' is not assignable to type 'AN'. kind: "A", n: { a: "a", diff --git a/tests/baselines/reference/nestedExcessPropertyChecks.errors.txt b/tests/baselines/reference/nestedExcessPropertyChecks.errors.txt new file mode 100644 index 0000000000000..0bd2003587866 --- /dev/null +++ b/tests/baselines/reference/nestedExcessPropertyChecks.errors.txt @@ -0,0 +1,192 @@ +tests/cases/compiler/nestedExcessPropertyChecks.ts(21,7): error TS2322: Type '{ startDate: Date; author: number; }' is not assignable to type 'AllowedOptions'. +tests/cases/compiler/nestedExcessPropertyChecks.ts(43,5): error TS2322: Type '{ a: { x: number; }; }' is not assignable to type 'B'. + Types of property 'a' are incompatible. + Type '{ x: number; }' is not assignable to type 'A'. + Types of property 'x' are incompatible. + Type 'number' is not assignable to type 'string'. +tests/cases/compiler/nestedExcessPropertyChecks.ts(44,29): error TS2322: Type '{ a: { x: string; y: number; }; }' is not assignable to type 'B'. + Object literal may only specify known properties, and 'y' does not exist in type 'B'. +tests/cases/compiler/nestedExcessPropertyChecks.ts(47,5): error TS2322: Type '{ a: { x: number; }; c: number; }' is not assignable to type 'D'. + Type '{ a: { x: number; }; c: number; }' is not assignable to type 'B'. + Types of property 'a' are incompatible. + Type '{ x: number; }' is not assignable to type 'A'. + Types of property 'x' are incompatible. + Type 'number' is not assignable to type 'string'. +tests/cases/compiler/nestedExcessPropertyChecks.ts(48,29): error TS2322: Type '{ a: { x: string; y: number; }; c: number; }' is not assignable to type 'D'. + Object literal may only specify known properties, and 'y' does not exist in type 'D'. +tests/cases/compiler/nestedExcessPropertyChecks.ts(68,9): error TS2322: Type '{ foo: number; __name: string; bar: { baz: string; not_exist_key: boolean; }; }' is not assignable to type 'Template<{ foo: number; bar: { baz: string; }; }>'. + Object literal may only specify known properties, and 'not_exist_key' does not exist in type 'Template<{ foo: number; bar: { baz: string; }; }>'. +tests/cases/compiler/nestedExcessPropertyChecks.ts(82,38): error TS2322: Type '{ x: string; y: boolean; }' is not assignable to type 'A'. + Object literal may only specify known properties, and 'y' does not exist in type 'A'. +tests/cases/compiler/nestedExcessPropertyChecks.ts(83,47): error TS2322: Type '{ x: string; y: boolean; }' is not assignable to type 'A & EmptyInterface'. + Object literal may only specify known properties, and 'y' does not exist in type 'A & EmptyInterface'. +tests/cases/compiler/nestedExcessPropertyChecks.ts(86,52): error TS2322: Type '{ x: { x: string; y: boolean; }; }' is not assignable to type '{ x: A; } & { x: Empty; }'. + Object literal may only specify known properties, and 'y' does not exist in type '{ x: A; } & { x: Empty; }'. +tests/cases/compiler/nestedExcessPropertyChecks.ts(87,61): error TS2322: Type '{ x: { x: string; y: boolean; }; }' is not assignable to type '{ x: A; } & { x: EmptyInterface; }'. + Object literal may only specify known properties, and 'y' does not exist in type '{ x: A; } & { x: EmptyInterface; }'. +tests/cases/compiler/nestedExcessPropertyChecks.ts(94,26): error TS2322: Type '{ y: string; }' is not assignable to type 'Nesting'. + Object literal may only specify known properties, and 'y' does not exist in type 'Nesting'. +tests/cases/compiler/nestedExcessPropertyChecks.ts(95,30): error TS2322: Type '{ x: { excess: boolean; }; }' is not assignable to type 'Nesting'. + Object literal may only specify known properties, and 'excess' does not exist in type 'Nesting'. +tests/cases/compiler/nestedExcessPropertyChecks.ts(96,5): error TS2322: Type '{ x: { a: true; b: string; y: true; z: string; }; }' is not assignable to type 'Nesting'. +tests/cases/compiler/nestedExcessPropertyChecks.ts(97,47): error TS2322: Type '{ x: { y: true; z: string; excess: boolean; }; }' is not assignable to type 'Nesting'. + Object literal may only specify known properties, and 'excess' does not exist in type 'Nesting'. +tests/cases/compiler/nestedExcessPropertyChecks.ts(103,5): error TS2322: Type '{ x: { y: string; z: true; q: true; }; }' is not assignable to type 'InnerUnion'. + Type '{ y: string; z: true; q: true; }' is not assignable to type '{ y: string; } | { z: boolean; }'. + + +==== tests/cases/compiler/nestedExcessPropertyChecks.ts (15 errors) ==== + // Repro #20863 + + // An object to hold all the possible options + type AllOptions = { + startDate: Date + endDate: Date + author: number + } + + // Any combination of startDate, endDate can be used + type DateOptions = + | Pick + | Pick + | Pick + + type AuthorOptions = Pick + + type AllowedOptions = DateOptions | AuthorOptions + + // options double dips + const options: AllowedOptions = { + ~~~~~~~ +!!! error TS2322: Type '{ startDate: Date; author: number; }' is not assignable to type 'AllowedOptions'. + startDate: new Date(), // error + author: 1 + } + + // Repro #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 '{ a: { x: number; }; }' is not assignable to type 'B'. +!!! error TS2322: Types of property 'a' are incompatible. +!!! error TS2322: Type '{ x: number; }' is not assignable to type 'A'. +!!! error TS2322: Types of property 'x' are incompatible. +!!! error TS2322: Type 'number' is not assignable to type 'string'. + let c: B = {a: {x: 'hello', y: 2}}; // error - y does not exist in type B + ~~~~ +!!! error TS2322: Type '{ a: { x: string; y: number; }; }' is not assignable to type 'B'. +!!! error TS2322: Object literal may only specify known properties, and 'y' does not exist in 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 '{ a: { x: number; }; c: number; }' is not assignable to type 'D'. +!!! error TS2322: Type '{ a: { x: number; }; c: number; }' is not assignable to type 'B'. +!!! error TS2322: Types of property 'a' are incompatible. +!!! error TS2322: Type '{ x: number; }' is not assignable to type 'A'. +!!! error TS2322: Types of property 'x' are incompatible. +!!! error TS2322: Type 'number' is not assignable to type 'string'. + let f: D = {a: {x: 'hello', y: 2}, c: 5}; // y does not exist in type D + ~~~~ +!!! error TS2322: Type '{ a: { x: string; y: number; }; c: number; }' is not assignable to type 'D'. +!!! error TS2322: Object literal may only specify known properties, and 'y' does not exist in type 'D'. + + // Repro #23706 + + type Template = { + [K in keyof T]: Template; + } & { + __name?: string; + } + + const template: Template<{ + foo: number; + bar: { + baz: string; + } + }> = { + foo: 1, + __name: 'n', + bar: { + baz: 'b', + not_exist_key: true // error + ~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ foo: number; __name: string; bar: { baz: string; not_exist_key: boolean; }; }' is not assignable to type 'Template<{ foo: number; bar: { baz: string; }; }>'. +!!! error TS2322: Object literal may only specify known properties, and 'not_exist_key' does not exist in type 'Template<{ foo: number; bar: { baz: string; }; }>'. + } + }; + + // Other tests + + // Empty alias normalises {} for intersection, but EmptyInterface will + // not. Check that they behave the same. + + type Empty = {}; + interface EmptyInterface {} + + let empty1: A & Empty = {x: "hello"}; + let empty2: A & EmptyInterface = {x: "hello"}; + let empty3: A & Empty = {x: "hello", y: true}; // error as A & Empty = A + ~~~~~~~ +!!! error TS2322: Type '{ x: string; y: boolean; }' is not assignable to type 'A'. +!!! error TS2322: Object literal may only specify known properties, and 'y' does not exist in type 'A'. + let empty4: A & EmptyInterface = {x: "hello", y: true}; // error as A & EmptyInterface is equivalent to A + ~~~~~~~ +!!! error TS2322: Type '{ x: string; y: boolean; }' is not assignable to type 'A & EmptyInterface'. +!!! error TS2322: Object literal may only specify known properties, and 'y' does not exist in type 'A & EmptyInterface'. + let empty5: {x: A} & {x: Empty} = {x: {x: "hello"}}; + let empty6: {x: A} & {x: EmptyInterface} = {x: {x: "hello"}}; + let empty7: {x: A} & {x: Empty} = {x: {x: "hello", y: true}}; // error + ~~~~~~~ +!!! error TS2322: Type '{ x: { x: string; y: boolean; }; }' is not assignable to type '{ x: A; } & { x: Empty; }'. +!!! error TS2322: Object literal may only specify known properties, and 'y' does not exist in type '{ x: A; } & { x: Empty; }'. + let empty8: {x: A} & {x: EmptyInterface} = {x: {x: "hello", y: true}}; // error + ~~~~~~~ +!!! error TS2322: Type '{ x: { x: string; y: boolean; }; }' is not assignable to type '{ x: A; } & { x: EmptyInterface; }'. +!!! error TS2322: Object literal may only specify known properties, and 'y' does not exist in type '{ x: A; } & { x: EmptyInterface; }'. + + // Nesting with intersection and union + + type Nesting = {x: {a: boolean, b: string}} | {x: {y: boolean} & {z: string} & {}}; + + let nesting1: Nesting = {x: {a: true, b: "b"}}; + let nesting2: Nesting = {y: "excess"}; // y is excess + ~~~~~~~~~~~ +!!! error TS2322: Type '{ y: string; }' is not assignable to type 'Nesting'. +!!! error TS2322: Object literal may only specify known properties, and 'y' does not exist in type 'Nesting'. + let nesting3: Nesting = {x: {excess: true}}; // excess is excess + ~~~~~~~~~~~~ +!!! error TS2322: Type '{ x: { excess: boolean; }; }' is not assignable to type 'Nesting'. +!!! error TS2322: Object literal may only specify known properties, and 'excess' does not exist in type 'Nesting'. + let nesting4: Nesting = {x: {a: true, b: "b", y: true, z: "hello"}}; // Double dipping on both sides of the union + ~~~~~~~~ +!!! error TS2322: Type '{ x: { a: true; b: string; y: true; z: string; }; }' is not assignable to type 'Nesting'. + let nesting5: Nesting = {x: {y: true, z: "b", excess: true}}; // excess is excess + ~~~~~~~~~~~~ +!!! error TS2322: Type '{ x: { y: true; z: string; excess: boolean; }; }' is not assignable to type 'Nesting'. +!!! error TS2322: Object literal may only specify known properties, and 'excess' does not exist in type 'Nesting'. + + + type InnerUnion = {x: ({y: string} | {z: boolean})} & {x: {q: boolean}}; + let innerUnion1: InnerUnion = {x: {y: "ok", q: true}}; + let innerUnion2: InnerUnion = {x: {z: true, q: true}}; + let innerUnion3: InnerUnion = {x: {y: "not ok", z: true, q: true}}; + ~~~~~~~~~~~ +!!! error TS2322: Type '{ x: { y: string; z: true; q: true; }; }' is not assignable to type 'InnerUnion'. +!!! error TS2322: Type '{ y: string; z: true; q: true; }' is not assignable to type '{ y: string; } | { z: boolean; }'. + \ No newline at end of file diff --git a/tests/baselines/reference/nestedExcessPropertyChecks.js b/tests/baselines/reference/nestedExcessPropertyChecks.js new file mode 100644 index 0000000000000..c2c1c86939a2d --- /dev/null +++ b/tests/baselines/reference/nestedExcessPropertyChecks.js @@ -0,0 +1,143 @@ +//// [nestedExcessPropertyChecks.ts] +// Repro #20863 + +// An object to hold all the possible options +type AllOptions = { + startDate: Date + endDate: Date + author: number +} + +// Any combination of startDate, endDate can be used +type DateOptions = + | Pick + | Pick + | Pick + +type AuthorOptions = Pick + +type AllowedOptions = DateOptions | AuthorOptions + +// options double dips +const options: AllowedOptions = { + startDate: new Date(), // error + author: 1 +} + +// Repro #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 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 +let f: D = {a: {x: 'hello', y: 2}, c: 5}; // y does not exist in type D + +// Repro #23706 + +type Template = { + [K in keyof T]: Template; +} & { + __name?: string; +} + +const template: Template<{ + foo: number; + bar: { + baz: string; + } +}> = { + foo: 1, + __name: 'n', + bar: { + baz: 'b', + not_exist_key: true // error + } +}; + +// Other tests + +// Empty alias normalises {} for intersection, but EmptyInterface will +// not. Check that they behave the same. + +type Empty = {}; +interface EmptyInterface {} + +let empty1: A & Empty = {x: "hello"}; +let empty2: A & EmptyInterface = {x: "hello"}; +let empty3: A & Empty = {x: "hello", y: true}; // error as A & Empty = A +let empty4: A & EmptyInterface = {x: "hello", y: true}; // error as A & EmptyInterface is equivalent to A +let empty5: {x: A} & {x: Empty} = {x: {x: "hello"}}; +let empty6: {x: A} & {x: EmptyInterface} = {x: {x: "hello"}}; +let empty7: {x: A} & {x: Empty} = {x: {x: "hello", y: true}}; // error +let empty8: {x: A} & {x: EmptyInterface} = {x: {x: "hello", y: true}}; // error + +// Nesting with intersection and union + +type Nesting = {x: {a: boolean, b: string}} | {x: {y: boolean} & {z: string} & {}}; + +let nesting1: Nesting = {x: {a: true, b: "b"}}; +let nesting2: Nesting = {y: "excess"}; // y is excess +let nesting3: Nesting = {x: {excess: true}}; // excess is excess +let nesting4: Nesting = {x: {a: true, b: "b", y: true, z: "hello"}}; // Double dipping on both sides of the union +let nesting5: Nesting = {x: {y: true, z: "b", excess: true}}; // excess is excess + + +type InnerUnion = {x: ({y: string} | {z: boolean})} & {x: {q: boolean}}; +let innerUnion1: InnerUnion = {x: {y: "ok", q: true}}; +let innerUnion2: InnerUnion = {x: {z: true, q: true}}; +let innerUnion3: InnerUnion = {x: {y: "not ok", z: true, q: true}}; + + +//// [nestedExcessPropertyChecks.js] +// Repro #20863 +// options double dips +var options = { + startDate: new Date(), + author: 1 +}; +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 B +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 }; // y does not exist in type D +var template = { + foo: 1, + __name: 'n', + bar: { + baz: 'b', + not_exist_key: true // error + } +}; +var empty1 = { x: "hello" }; +var empty2 = { x: "hello" }; +var empty3 = { x: "hello", y: true }; // error as A & Empty = A +var empty4 = { x: "hello", y: true }; // error as A & EmptyInterface is equivalent to A +var empty5 = { x: { x: "hello" } }; +var empty6 = { x: { x: "hello" } }; +var empty7 = { x: { x: "hello", y: true } }; // error +var empty8 = { x: { x: "hello", y: true } }; // error +var nesting1 = { x: { a: true, b: "b" } }; +var nesting2 = { y: "excess" }; // y is excess +var nesting3 = { x: { excess: true } }; // excess is excess +var nesting4 = { x: { a: true, b: "b", y: true, z: "hello" } }; // Double dipping on both sides of the union +var nesting5 = { x: { y: true, z: "b", excess: true } }; // excess is excess +var innerUnion1 = { x: { y: "ok", q: true } }; +var innerUnion2 = { x: { z: true, q: true } }; +var innerUnion3 = { x: { y: "not ok", z: true, q: true } }; diff --git a/tests/baselines/reference/nestedExcessPropertyChecks.symbols b/tests/baselines/reference/nestedExcessPropertyChecks.symbols new file mode 100644 index 0000000000000..11d7a382727f2 --- /dev/null +++ b/tests/baselines/reference/nestedExcessPropertyChecks.symbols @@ -0,0 +1,329 @@ +=== tests/cases/compiler/nestedExcessPropertyChecks.ts === +// Repro #20863 + +// An object to hold all the possible options +type AllOptions = { +>AllOptions : Symbol(AllOptions, Decl(nestedExcessPropertyChecks.ts, 0, 0)) + + startDate: Date +>startDate : Symbol(startDate, Decl(nestedExcessPropertyChecks.ts, 3, 19)) +>Date : Symbol(Date, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + endDate: Date +>endDate : Symbol(endDate, Decl(nestedExcessPropertyChecks.ts, 4, 19)) +>Date : Symbol(Date, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + author: number +>author : Symbol(author, Decl(nestedExcessPropertyChecks.ts, 5, 17)) +} + +// Any combination of startDate, endDate can be used +type DateOptions = +>DateOptions : Symbol(DateOptions, Decl(nestedExcessPropertyChecks.ts, 7, 1)) + + | Pick +>Pick : Symbol(Pick, Decl(lib.d.ts, --, --)) +>AllOptions : Symbol(AllOptions, Decl(nestedExcessPropertyChecks.ts, 0, 0)) + + | Pick +>Pick : Symbol(Pick, Decl(lib.d.ts, --, --)) +>AllOptions : Symbol(AllOptions, Decl(nestedExcessPropertyChecks.ts, 0, 0)) + + | Pick +>Pick : Symbol(Pick, Decl(lib.d.ts, --, --)) +>AllOptions : Symbol(AllOptions, Decl(nestedExcessPropertyChecks.ts, 0, 0)) + +type AuthorOptions = Pick +>AuthorOptions : Symbol(AuthorOptions, Decl(nestedExcessPropertyChecks.ts, 13, 47)) +>Pick : Symbol(Pick, Decl(lib.d.ts, --, --)) +>AllOptions : Symbol(AllOptions, Decl(nestedExcessPropertyChecks.ts, 0, 0)) + +type AllowedOptions = DateOptions | AuthorOptions +>AllowedOptions : Symbol(AllowedOptions, Decl(nestedExcessPropertyChecks.ts, 15, 47)) +>DateOptions : Symbol(DateOptions, Decl(nestedExcessPropertyChecks.ts, 7, 1)) +>AuthorOptions : Symbol(AuthorOptions, Decl(nestedExcessPropertyChecks.ts, 13, 47)) + +// options double dips +const options: AllowedOptions = { +>options : Symbol(options, Decl(nestedExcessPropertyChecks.ts, 20, 5)) +>AllowedOptions : Symbol(AllowedOptions, Decl(nestedExcessPropertyChecks.ts, 15, 47)) + + startDate: new Date(), // error +>startDate : Symbol(startDate, Decl(nestedExcessPropertyChecks.ts, 20, 33)) +>Date : Symbol(Date, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + author: 1 +>author : Symbol(author, Decl(nestedExcessPropertyChecks.ts, 21, 26)) +} + +// Repro #13813 + +interface A { +>A : Symbol(A, Decl(nestedExcessPropertyChecks.ts, 23, 1)) + + x: string +>x : Symbol(A.x, Decl(nestedExcessPropertyChecks.ts, 27, 13)) +} + +interface B { +>B : Symbol(B, Decl(nestedExcessPropertyChecks.ts, 29, 1)) + + a: A; +>a : Symbol(B.a, Decl(nestedExcessPropertyChecks.ts, 31, 13)) +>A : Symbol(A, Decl(nestedExcessPropertyChecks.ts, 23, 1)) +} + +interface C { +>C : Symbol(C, Decl(nestedExcessPropertyChecks.ts, 33, 1)) + + c: number; +>c : Symbol(C.c, Decl(nestedExcessPropertyChecks.ts, 35, 13)) +} + +type D = B & C; +>D : Symbol(D, Decl(nestedExcessPropertyChecks.ts, 37, 1)) +>B : Symbol(B, Decl(nestedExcessPropertyChecks.ts, 29, 1)) +>C : Symbol(C, Decl(nestedExcessPropertyChecks.ts, 33, 1)) + +let a: B = {a: {x: 'hello'}}; // ok +>a : Symbol(a, Decl(nestedExcessPropertyChecks.ts, 41, 3)) +>B : Symbol(B, Decl(nestedExcessPropertyChecks.ts, 29, 1)) +>a : Symbol(a, Decl(nestedExcessPropertyChecks.ts, 41, 12)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 41, 16)) + +let b: B = {a: {x: 2}}; // error - types of property x are incompatible +>b : Symbol(b, Decl(nestedExcessPropertyChecks.ts, 42, 3)) +>B : Symbol(B, Decl(nestedExcessPropertyChecks.ts, 29, 1)) +>a : Symbol(a, Decl(nestedExcessPropertyChecks.ts, 42, 12)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 42, 16)) + +let c: B = {a: {x: 'hello', y: 2}}; // error - y does not exist in type B +>c : Symbol(c, Decl(nestedExcessPropertyChecks.ts, 43, 3)) +>B : Symbol(B, Decl(nestedExcessPropertyChecks.ts, 29, 1)) +>a : Symbol(a, Decl(nestedExcessPropertyChecks.ts, 43, 12)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 43, 16)) +>y : Symbol(y, Decl(nestedExcessPropertyChecks.ts, 43, 27)) + +let d: D = {a: {x: 'hello'}, c: 5}; // ok +>d : Symbol(d, Decl(nestedExcessPropertyChecks.ts, 45, 3)) +>D : Symbol(D, Decl(nestedExcessPropertyChecks.ts, 37, 1)) +>a : Symbol(a, Decl(nestedExcessPropertyChecks.ts, 45, 12)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 45, 16)) +>c : Symbol(c, Decl(nestedExcessPropertyChecks.ts, 45, 28)) + +let e: D = {a: {x: 2}, c: 5}; // error - types of property x are incompatible +>e : Symbol(e, Decl(nestedExcessPropertyChecks.ts, 46, 3)) +>D : Symbol(D, Decl(nestedExcessPropertyChecks.ts, 37, 1)) +>a : Symbol(a, Decl(nestedExcessPropertyChecks.ts, 46, 12)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 46, 16)) +>c : Symbol(c, Decl(nestedExcessPropertyChecks.ts, 46, 22)) + +let f: D = {a: {x: 'hello', y: 2}, c: 5}; // y does not exist in type D +>f : Symbol(f, Decl(nestedExcessPropertyChecks.ts, 47, 3)) +>D : Symbol(D, Decl(nestedExcessPropertyChecks.ts, 37, 1)) +>a : Symbol(a, Decl(nestedExcessPropertyChecks.ts, 47, 12)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 47, 16)) +>y : Symbol(y, Decl(nestedExcessPropertyChecks.ts, 47, 27)) +>c : Symbol(c, Decl(nestedExcessPropertyChecks.ts, 47, 34)) + +// Repro #23706 + +type Template = { +>Template : Symbol(Template, Decl(nestedExcessPropertyChecks.ts, 47, 41)) +>T : Symbol(T, Decl(nestedExcessPropertyChecks.ts, 51, 14)) + + [K in keyof T]: Template; +>K : Symbol(K, Decl(nestedExcessPropertyChecks.ts, 52, 5)) +>T : Symbol(T, Decl(nestedExcessPropertyChecks.ts, 51, 14)) +>Template : Symbol(Template, Decl(nestedExcessPropertyChecks.ts, 47, 41)) +>T : Symbol(T, Decl(nestedExcessPropertyChecks.ts, 51, 14)) +>K : Symbol(K, Decl(nestedExcessPropertyChecks.ts, 52, 5)) + +} & { + __name?: string; +>__name : Symbol(__name, Decl(nestedExcessPropertyChecks.ts, 53, 5)) +} + +const template: Template<{ +>template : Symbol(template, Decl(nestedExcessPropertyChecks.ts, 57, 5)) +>Template : Symbol(Template, Decl(nestedExcessPropertyChecks.ts, 47, 41)) + + foo: number; +>foo : Symbol(foo, Decl(nestedExcessPropertyChecks.ts, 57, 26)) + + bar: { +>bar : Symbol(bar, Decl(nestedExcessPropertyChecks.ts, 58, 16)) + + baz: string; +>baz : Symbol(baz, Decl(nestedExcessPropertyChecks.ts, 59, 10)) + } +}> = { + foo: 1, +>foo : Symbol(foo, Decl(nestedExcessPropertyChecks.ts, 62, 6)) + + __name: 'n', +>__name : Symbol(__name, Decl(nestedExcessPropertyChecks.ts, 63, 11)) + + bar: { +>bar : Symbol(bar, Decl(nestedExcessPropertyChecks.ts, 64, 16)) + + baz: 'b', +>baz : Symbol(baz, Decl(nestedExcessPropertyChecks.ts, 65, 10)) + + not_exist_key: true // error +>not_exist_key : Symbol(not_exist_key, Decl(nestedExcessPropertyChecks.ts, 66, 17)) + } +}; + +// Other tests + +// Empty alias normalises {} for intersection, but EmptyInterface will +// not. Check that they behave the same. + +type Empty = {}; +>Empty : Symbol(Empty, Decl(nestedExcessPropertyChecks.ts, 69, 2)) + +interface EmptyInterface {} +>EmptyInterface : Symbol(EmptyInterface, Decl(nestedExcessPropertyChecks.ts, 76, 16)) + +let empty1: A & Empty = {x: "hello"}; +>empty1 : Symbol(empty1, Decl(nestedExcessPropertyChecks.ts, 79, 3)) +>A : Symbol(A, Decl(nestedExcessPropertyChecks.ts, 23, 1)) +>Empty : Symbol(Empty, Decl(nestedExcessPropertyChecks.ts, 69, 2)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 79, 25)) + +let empty2: A & EmptyInterface = {x: "hello"}; +>empty2 : Symbol(empty2, Decl(nestedExcessPropertyChecks.ts, 80, 3)) +>A : Symbol(A, Decl(nestedExcessPropertyChecks.ts, 23, 1)) +>EmptyInterface : Symbol(EmptyInterface, Decl(nestedExcessPropertyChecks.ts, 76, 16)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 80, 34)) + +let empty3: A & Empty = {x: "hello", y: true}; // error as A & Empty = A +>empty3 : Symbol(empty3, Decl(nestedExcessPropertyChecks.ts, 81, 3)) +>A : Symbol(A, Decl(nestedExcessPropertyChecks.ts, 23, 1)) +>Empty : Symbol(Empty, Decl(nestedExcessPropertyChecks.ts, 69, 2)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 81, 25)) +>y : Symbol(y, Decl(nestedExcessPropertyChecks.ts, 81, 36)) + +let empty4: A & EmptyInterface = {x: "hello", y: true}; // error as A & EmptyInterface is equivalent to A +>empty4 : Symbol(empty4, Decl(nestedExcessPropertyChecks.ts, 82, 3)) +>A : Symbol(A, Decl(nestedExcessPropertyChecks.ts, 23, 1)) +>EmptyInterface : Symbol(EmptyInterface, Decl(nestedExcessPropertyChecks.ts, 76, 16)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 82, 34)) +>y : Symbol(y, Decl(nestedExcessPropertyChecks.ts, 82, 45)) + +let empty5: {x: A} & {x: Empty} = {x: {x: "hello"}}; +>empty5 : Symbol(empty5, Decl(nestedExcessPropertyChecks.ts, 83, 3)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 83, 13)) +>A : Symbol(A, Decl(nestedExcessPropertyChecks.ts, 23, 1)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 83, 22)) +>Empty : Symbol(Empty, Decl(nestedExcessPropertyChecks.ts, 69, 2)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 83, 35)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 83, 39)) + +let empty6: {x: A} & {x: EmptyInterface} = {x: {x: "hello"}}; +>empty6 : Symbol(empty6, Decl(nestedExcessPropertyChecks.ts, 84, 3)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 84, 13)) +>A : Symbol(A, Decl(nestedExcessPropertyChecks.ts, 23, 1)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 84, 22)) +>EmptyInterface : Symbol(EmptyInterface, Decl(nestedExcessPropertyChecks.ts, 76, 16)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 84, 44)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 84, 48)) + +let empty7: {x: A} & {x: Empty} = {x: {x: "hello", y: true}}; // error +>empty7 : Symbol(empty7, Decl(nestedExcessPropertyChecks.ts, 85, 3)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 85, 13)) +>A : Symbol(A, Decl(nestedExcessPropertyChecks.ts, 23, 1)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 85, 22)) +>Empty : Symbol(Empty, Decl(nestedExcessPropertyChecks.ts, 69, 2)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 85, 35)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 85, 39)) +>y : Symbol(y, Decl(nestedExcessPropertyChecks.ts, 85, 50)) + +let empty8: {x: A} & {x: EmptyInterface} = {x: {x: "hello", y: true}}; // error +>empty8 : Symbol(empty8, Decl(nestedExcessPropertyChecks.ts, 86, 3)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 86, 13)) +>A : Symbol(A, Decl(nestedExcessPropertyChecks.ts, 23, 1)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 86, 22)) +>EmptyInterface : Symbol(EmptyInterface, Decl(nestedExcessPropertyChecks.ts, 76, 16)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 86, 44)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 86, 48)) +>y : Symbol(y, Decl(nestedExcessPropertyChecks.ts, 86, 59)) + +// Nesting with intersection and union + +type Nesting = {x: {a: boolean, b: string}} | {x: {y: boolean} & {z: string} & {}}; +>Nesting : Symbol(Nesting, Decl(nestedExcessPropertyChecks.ts, 86, 70)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 90, 16)) +>a : Symbol(a, Decl(nestedExcessPropertyChecks.ts, 90, 20)) +>b : Symbol(b, Decl(nestedExcessPropertyChecks.ts, 90, 31)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 90, 47)) +>y : Symbol(y, Decl(nestedExcessPropertyChecks.ts, 90, 51)) +>z : Symbol(z, Decl(nestedExcessPropertyChecks.ts, 90, 66)) + +let nesting1: Nesting = {x: {a: true, b: "b"}}; +>nesting1 : Symbol(nesting1, Decl(nestedExcessPropertyChecks.ts, 92, 3)) +>Nesting : Symbol(Nesting, Decl(nestedExcessPropertyChecks.ts, 86, 70)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 92, 25)) +>a : Symbol(a, Decl(nestedExcessPropertyChecks.ts, 92, 29)) +>b : Symbol(b, Decl(nestedExcessPropertyChecks.ts, 92, 37)) + +let nesting2: Nesting = {y: "excess"}; // y is excess +>nesting2 : Symbol(nesting2, Decl(nestedExcessPropertyChecks.ts, 93, 3)) +>Nesting : Symbol(Nesting, Decl(nestedExcessPropertyChecks.ts, 86, 70)) +>y : Symbol(y, Decl(nestedExcessPropertyChecks.ts, 93, 25)) + +let nesting3: Nesting = {x: {excess: true}}; // excess is excess +>nesting3 : Symbol(nesting3, Decl(nestedExcessPropertyChecks.ts, 94, 3)) +>Nesting : Symbol(Nesting, Decl(nestedExcessPropertyChecks.ts, 86, 70)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 94, 25)) +>excess : Symbol(excess, Decl(nestedExcessPropertyChecks.ts, 94, 29)) + +let nesting4: Nesting = {x: {a: true, b: "b", y: true, z: "hello"}}; // Double dipping on both sides of the union +>nesting4 : Symbol(nesting4, Decl(nestedExcessPropertyChecks.ts, 95, 3)) +>Nesting : Symbol(Nesting, Decl(nestedExcessPropertyChecks.ts, 86, 70)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 95, 25)) +>a : Symbol(a, Decl(nestedExcessPropertyChecks.ts, 95, 29)) +>b : Symbol(b, Decl(nestedExcessPropertyChecks.ts, 95, 37)) +>y : Symbol(y, Decl(nestedExcessPropertyChecks.ts, 95, 45)) +>z : Symbol(z, Decl(nestedExcessPropertyChecks.ts, 95, 54)) + +let nesting5: Nesting = {x: {y: true, z: "b", excess: true}}; // excess is excess +>nesting5 : Symbol(nesting5, Decl(nestedExcessPropertyChecks.ts, 96, 3)) +>Nesting : Symbol(Nesting, Decl(nestedExcessPropertyChecks.ts, 86, 70)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 96, 25)) +>y : Symbol(y, Decl(nestedExcessPropertyChecks.ts, 96, 29)) +>z : Symbol(z, Decl(nestedExcessPropertyChecks.ts, 96, 37)) +>excess : Symbol(excess, Decl(nestedExcessPropertyChecks.ts, 96, 45)) + + +type InnerUnion = {x: ({y: string} | {z: boolean})} & {x: {q: boolean}}; +>InnerUnion : Symbol(InnerUnion, Decl(nestedExcessPropertyChecks.ts, 96, 61)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 99, 19)) +>y : Symbol(y, Decl(nestedExcessPropertyChecks.ts, 99, 24)) +>z : Symbol(z, Decl(nestedExcessPropertyChecks.ts, 99, 38)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 99, 55)) +>q : Symbol(q, Decl(nestedExcessPropertyChecks.ts, 99, 59)) + +let innerUnion1: InnerUnion = {x: {y: "ok", q: true}}; +>innerUnion1 : Symbol(innerUnion1, Decl(nestedExcessPropertyChecks.ts, 100, 3)) +>InnerUnion : Symbol(InnerUnion, Decl(nestedExcessPropertyChecks.ts, 96, 61)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 100, 31)) +>y : Symbol(y, Decl(nestedExcessPropertyChecks.ts, 100, 35)) +>q : Symbol(q, Decl(nestedExcessPropertyChecks.ts, 100, 43)) + +let innerUnion2: InnerUnion = {x: {z: true, q: true}}; +>innerUnion2 : Symbol(innerUnion2, Decl(nestedExcessPropertyChecks.ts, 101, 3)) +>InnerUnion : Symbol(InnerUnion, Decl(nestedExcessPropertyChecks.ts, 96, 61)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 101, 31)) +>z : Symbol(z, Decl(nestedExcessPropertyChecks.ts, 101, 35)) +>q : Symbol(q, Decl(nestedExcessPropertyChecks.ts, 101, 43)) + +let innerUnion3: InnerUnion = {x: {y: "not ok", z: true, q: true}}; +>innerUnion3 : Symbol(innerUnion3, Decl(nestedExcessPropertyChecks.ts, 102, 3)) +>InnerUnion : Symbol(InnerUnion, Decl(nestedExcessPropertyChecks.ts, 96, 61)) +>x : Symbol(x, Decl(nestedExcessPropertyChecks.ts, 102, 31)) +>y : Symbol(y, Decl(nestedExcessPropertyChecks.ts, 102, 35)) +>z : Symbol(z, Decl(nestedExcessPropertyChecks.ts, 102, 47)) +>q : Symbol(q, Decl(nestedExcessPropertyChecks.ts, 102, 56)) + diff --git a/tests/baselines/reference/nestedExcessPropertyChecks.types b/tests/baselines/reference/nestedExcessPropertyChecks.types new file mode 100644 index 0000000000000..0d75e3ee8a6f4 --- /dev/null +++ b/tests/baselines/reference/nestedExcessPropertyChecks.types @@ -0,0 +1,419 @@ +=== tests/cases/compiler/nestedExcessPropertyChecks.ts === +// Repro #20863 + +// An object to hold all the possible options +type AllOptions = { +>AllOptions : AllOptions + + startDate: Date +>startDate : Date +>Date : Date + + endDate: Date +>endDate : Date +>Date : Date + + author: number +>author : number +} + +// Any combination of startDate, endDate can be used +type DateOptions = +>DateOptions : DateOptions + + | Pick +>Pick : Pick +>AllOptions : AllOptions + + | Pick +>Pick : Pick +>AllOptions : AllOptions + + | Pick +>Pick : Pick +>AllOptions : AllOptions + +type AuthorOptions = Pick +>AuthorOptions : Pick +>Pick : Pick +>AllOptions : AllOptions + +type AllowedOptions = DateOptions | AuthorOptions +>AllowedOptions : AllowedOptions +>DateOptions : DateOptions +>AuthorOptions : Pick + +// options double dips +const options: AllowedOptions = { +>options : AllowedOptions +>AllowedOptions : AllowedOptions +>{ startDate: new Date(), // error author: 1} : { startDate: Date; author: number; } + + startDate: new Date(), // error +>startDate : Date +>new Date() : Date +>Date : DateConstructor + + author: 1 +>author : number +>1 : 1 +} + +// Repro #13813 + +interface A { +>A : A + + x: string +>x : string +} + +interface B { +>B : B + + a: A; +>a : A +>A : A +} + +interface C { +>C : C + + c: number; +>c : number +} + +type D = B & C; +>D : D +>B : B +>C : C + +let a: B = {a: {x: 'hello'}}; // ok +>a : B +>B : 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 +>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 B +>c : B +>B : 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 +>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 +>D : 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}; // y does not exist in type D +>f : D +>D : 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 + +// Repro #23706 + +type Template = { +>Template : Template +>T : T + + [K in keyof T]: Template; +>K : K +>T : T +>Template : Template +>T : T +>K : K + +} & { + __name?: string; +>__name : string +} + +const template: Template<{ +>template : Template<{ foo: number; bar: { baz: string; }; }> +>Template : Template + + foo: number; +>foo : number + + bar: { +>bar : { baz: string; } + + baz: string; +>baz : string + } +}> = { +>{ foo: 1, __name: 'n', bar: { baz: 'b', not_exist_key: true // error }} : { foo: number; __name: string; bar: { baz: string; not_exist_key: boolean; }; } + + foo: 1, +>foo : number +>1 : 1 + + __name: 'n', +>__name : string +>'n' : "n" + + bar: { +>bar : { baz: string; not_exist_key: boolean; } +>{ baz: 'b', not_exist_key: true // error } : { baz: string; not_exist_key: boolean; } + + baz: 'b', +>baz : string +>'b' : "b" + + not_exist_key: true // error +>not_exist_key : boolean +>true : true + } +}; + +// Other tests + +// Empty alias normalises {} for intersection, but EmptyInterface will +// not. Check that they behave the same. + +type Empty = {}; +>Empty : Empty + +interface EmptyInterface {} +>EmptyInterface : EmptyInterface + +let empty1: A & Empty = {x: "hello"}; +>empty1 : A +>A : A +>Empty : Empty +>{x: "hello"} : { x: string; } +>x : string +>"hello" : "hello" + +let empty2: A & EmptyInterface = {x: "hello"}; +>empty2 : A & EmptyInterface +>A : A +>EmptyInterface : EmptyInterface +>{x: "hello"} : { x: string; } +>x : string +>"hello" : "hello" + +let empty3: A & Empty = {x: "hello", y: true}; // error as A & Empty = A +>empty3 : A +>A : A +>Empty : Empty +>{x: "hello", y: true} : { x: string; y: boolean; } +>x : string +>"hello" : "hello" +>y : boolean +>true : true + +let empty4: A & EmptyInterface = {x: "hello", y: true}; // error as A & EmptyInterface is equivalent to A +>empty4 : A & EmptyInterface +>A : A +>EmptyInterface : EmptyInterface +>{x: "hello", y: true} : { x: string; y: boolean; } +>x : string +>"hello" : "hello" +>y : boolean +>true : true + +let empty5: {x: A} & {x: Empty} = {x: {x: "hello"}}; +>empty5 : { x: A; } & { x: Empty; } +>x : A +>A : A +>x : Empty +>Empty : Empty +>{x: {x: "hello"}} : { x: { x: string; }; } +>x : { x: string; } +>{x: "hello"} : { x: string; } +>x : string +>"hello" : "hello" + +let empty6: {x: A} & {x: EmptyInterface} = {x: {x: "hello"}}; +>empty6 : { x: A; } & { x: EmptyInterface; } +>x : A +>A : A +>x : EmptyInterface +>EmptyInterface : EmptyInterface +>{x: {x: "hello"}} : { x: { x: string; }; } +>x : { x: string; } +>{x: "hello"} : { x: string; } +>x : string +>"hello" : "hello" + +let empty7: {x: A} & {x: Empty} = {x: {x: "hello", y: true}}; // error +>empty7 : { x: A; } & { x: Empty; } +>x : A +>A : A +>x : Empty +>Empty : Empty +>{x: {x: "hello", y: true}} : { x: { x: string; y: boolean; }; } +>x : { x: string; y: boolean; } +>{x: "hello", y: true} : { x: string; y: boolean; } +>x : string +>"hello" : "hello" +>y : boolean +>true : true + +let empty8: {x: A} & {x: EmptyInterface} = {x: {x: "hello", y: true}}; // error +>empty8 : { x: A; } & { x: EmptyInterface; } +>x : A +>A : A +>x : EmptyInterface +>EmptyInterface : EmptyInterface +>{x: {x: "hello", y: true}} : { x: { x: string; y: boolean; }; } +>x : { x: string; y: boolean; } +>{x: "hello", y: true} : { x: string; y: boolean; } +>x : string +>"hello" : "hello" +>y : boolean +>true : true + +// Nesting with intersection and union + +type Nesting = {x: {a: boolean, b: string}} | {x: {y: boolean} & {z: string} & {}}; +>Nesting : Nesting +>x : { a: boolean; b: string; } +>a : boolean +>b : string +>x : { y: boolean; } & { z: string; } +>y : boolean +>z : string + +let nesting1: Nesting = {x: {a: true, b: "b"}}; +>nesting1 : Nesting +>Nesting : Nesting +>{x: {a: true, b: "b"}} : { x: { a: true; b: string; }; } +>x : { a: true; b: string; } +>{a: true, b: "b"} : { a: true; b: string; } +>a : true +>true : true +>b : string +>"b" : "b" + +let nesting2: Nesting = {y: "excess"}; // y is excess +>nesting2 : Nesting +>Nesting : Nesting +>{y: "excess"} : { y: string; } +>y : string +>"excess" : "excess" + +let nesting3: Nesting = {x: {excess: true}}; // excess is excess +>nesting3 : Nesting +>Nesting : Nesting +>{x: {excess: true}} : { x: { excess: boolean; }; } +>x : { excess: boolean; } +>{excess: true} : { excess: boolean; } +>excess : boolean +>true : true + +let nesting4: Nesting = {x: {a: true, b: "b", y: true, z: "hello"}}; // Double dipping on both sides of the union +>nesting4 : Nesting +>Nesting : Nesting +>{x: {a: true, b: "b", y: true, z: "hello"}} : { x: { a: true; b: string; y: true; z: string; }; } +>x : { a: true; b: string; y: true; z: string; } +>{a: true, b: "b", y: true, z: "hello"} : { a: true; b: string; y: true; z: string; } +>a : true +>true : true +>b : string +>"b" : "b" +>y : true +>true : true +>z : string +>"hello" : "hello" + +let nesting5: Nesting = {x: {y: true, z: "b", excess: true}}; // excess is excess +>nesting5 : Nesting +>Nesting : Nesting +>{x: {y: true, z: "b", excess: true}} : { x: { y: true; z: string; excess: boolean; }; } +>x : { y: true; z: string; excess: boolean; } +>{y: true, z: "b", excess: true} : { y: true; z: string; excess: boolean; } +>y : true +>true : true +>z : string +>"b" : "b" +>excess : boolean +>true : true + + +type InnerUnion = {x: ({y: string} | {z: boolean})} & {x: {q: boolean}}; +>InnerUnion : InnerUnion +>x : { y: string; } | { z: boolean; } +>y : string +>z : boolean +>x : { q: boolean; } +>q : boolean + +let innerUnion1: InnerUnion = {x: {y: "ok", q: true}}; +>innerUnion1 : InnerUnion +>InnerUnion : InnerUnion +>{x: {y: "ok", q: true}} : { x: { y: string; q: true; }; } +>x : { y: string; q: true; } +>{y: "ok", q: true} : { y: string; q: true; } +>y : string +>"ok" : "ok" +>q : true +>true : true + +let innerUnion2: InnerUnion = {x: {z: true, q: true}}; +>innerUnion2 : InnerUnion +>InnerUnion : InnerUnion +>{x: {z: true, q: true}} : { x: { z: true; q: true; }; } +>x : { z: true; q: true; } +>{z: true, q: true} : { z: true; q: true; } +>z : true +>true : true +>q : true +>true : true + +let innerUnion3: InnerUnion = {x: {y: "not ok", z: true, q: true}}; +>innerUnion3 : InnerUnion +>InnerUnion : InnerUnion +>{x: {y: "not ok", z: true, q: true}} : { x: { y: string; z: true; q: true; }; } +>x : { y: string; z: true; q: true; } +>{y: "not ok", z: true, q: true} : { y: string; z: true; q: true; } +>y : string +>"not ok" : "not ok" +>z : true +>true : true +>q : true +>true : true + diff --git a/tests/baselines/reference/nestedFreshLiteral.errors.txt b/tests/baselines/reference/nestedFreshLiteral.errors.txt index d68b398449d53..9f99193672c17 100644 --- a/tests/baselines/reference/nestedFreshLiteral.errors.txt +++ b/tests/baselines/reference/nestedFreshLiteral.errors.txt @@ -1,10 +1,5 @@ tests/cases/compiler/nestedFreshLiteral.ts(12,21): error TS2322: Type '{ nested: { prop: { colour: string; }; }; }' is not assignable to type 'NestedCSSProps'. - Types of property 'nested' are incompatible. - Type '{ prop: { colour: string; }; }' is not assignable to type 'NestedSelector | undefined'. - Type '{ prop: { colour: string; }; }' is not assignable to type 'NestedSelector'. - Types of property 'prop' are incompatible. - Type '{ colour: string; }' is not assignable to type 'CSSProps'. - Object literal may only specify known properties, but 'colour' does not exist in type 'CSSProps'. Did you mean to write 'color'? + Object literal may only specify known properties, and 'colour' does not exist in type 'NestedCSSProps'. ==== tests/cases/compiler/nestedFreshLiteral.ts (1 errors) ==== @@ -22,10 +17,5 @@ tests/cases/compiler/nestedFreshLiteral.ts(12,21): error TS2322: Type '{ nested: nested: { prop: { colour: 'red' } } ~~~~~~~~~~~~~ !!! error TS2322: Type '{ nested: { prop: { colour: string; }; }; }' is not assignable to type 'NestedCSSProps'. -!!! error TS2322: Types of property 'nested' are incompatible. -!!! error TS2322: Type '{ prop: { colour: string; }; }' is not assignable to type 'NestedSelector | undefined'. -!!! error TS2322: Type '{ prop: { colour: string; }; }' is not assignable to type 'NestedSelector'. -!!! error TS2322: Types of property 'prop' are incompatible. -!!! error TS2322: Type '{ colour: string; }' is not assignable to type 'CSSProps'. -!!! error TS2322: Object literal may only specify known properties, but 'colour' does not exist in type 'CSSProps'. Did you mean to write 'color'? +!!! error TS2322: Object literal may only specify known properties, and 'colour' does not exist in type 'NestedCSSProps'. } \ No newline at end of file diff --git a/tests/baselines/reference/numericIndexerConstrainsPropertyDeclarations.errors.txt b/tests/baselines/reference/numericIndexerConstrainsPropertyDeclarations.errors.txt index f532bd237559d..505589dede869 100644 --- a/tests/baselines/reference/numericIndexerConstrainsPropertyDeclarations.errors.txt +++ b/tests/baselines/reference/numericIndexerConstrainsPropertyDeclarations.errors.txt @@ -5,10 +5,10 @@ tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerCo tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(36,16): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(50,5): error TS2412: Property '2.0' of type 'number' is not assignable to numeric index type 'string'. tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(68,5): error TS2412: Property '2.0' of type 'number' is not assignable to numeric index type 'string'. -tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(79,5): error TS2322: Type '{ a: string; b: number; c: () => void; "d": string; "e": number; 1.0: string; 2.0: number; "3.0": string; "4.0": number; f: any; X: string; foo(): string; }' is not assignable to type '{ [x: number]: string; }'. - Object literal may only specify known properties, and 'a' does not exist in type '{ [x: number]: string; }'. tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(88,9): error TS2304: Cannot find name 'Myn'. tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(90,9): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. +tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(90,9): error TS2322: Type '{ a: string; b: number; c: () => void; "d": string; "e": number; 1.0: string; 2.0: number; "3.0": string; "4.0": number; f: any; X: string; foo(): string; }' is not assignable to type '{ [x: number]: string; }'. + Object literal may only specify known properties, and 'X' does not exist in type '{ [x: number]: string; }'. tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(93,9): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. @@ -106,9 +106,6 @@ tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerCo // error var b: { [x: number]: string; } = { a: '', - ~~~~~ -!!! error TS2322: Type '{ a: string; b: number; c: () => void; "d": string; "e": number; 1.0: string; 2.0: number; "3.0": string; "4.0": number; f: any; X: string; foo(): string; }' is not assignable to type '{ [x: number]: string; }'. -!!! error TS2322: Object literal may only specify known properties, and 'a' does not exist in type '{ [x: number]: string; }'. b: 1, c: () => { }, "d": '', @@ -124,6 +121,9 @@ tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerCo get X() { ~ !!! error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. + ~ +!!! error TS2322: Type '{ a: string; b: number; c: () => void; "d": string; "e": number; 1.0: string; 2.0: number; "3.0": string; "4.0": number; f: any; X: string; foo(): string; }' is not assignable to type '{ [x: number]: string; }'. +!!! error TS2322: Object literal may only specify known properties, and 'X' does not exist in type '{ [x: number]: string; }'. return ''; }, set X(v) { }, diff --git a/tests/baselines/reference/objectLiteralExcessProperties.errors.txt b/tests/baselines/reference/objectLiteralExcessProperties.errors.txt index 0c5909dc4ed45..e4aaea06a9ddc 100644 --- a/tests/baselines/reference/objectLiteralExcessProperties.errors.txt +++ b/tests/baselines/reference/objectLiteralExcessProperties.errors.txt @@ -13,16 +13,15 @@ tests/cases/compiler/objectLiteralExcessProperties.ts(17,26): error TS2322: Type Object literal may only specify known properties, but 'foreward' does not exist in type 'Book & Cover'. Did you mean to write 'foreword'? tests/cases/compiler/objectLiteralExcessProperties.ts(19,57): error TS2322: Type '{ foreword: string; color: string; price: number; }' is not assignable to type 'Book & Cover'. Object literal may only specify known properties, and 'price' does not exist in type 'Book & Cover'. -tests/cases/compiler/objectLiteralExcessProperties.ts(21,43): error TS2322: Type '{ foreword: string; price: number; }' is not assignable to type 'Book & number'. - Object literal may only specify known properties, and 'price' does not exist in type 'Book & number'. +tests/cases/compiler/objectLiteralExcessProperties.ts(21,5): error TS2322: Type '{ foreword: string; price: number; }' is not assignable to type 'Book & number'. + Type '{ foreword: string; price: number; }' is not assignable to type 'number'. tests/cases/compiler/objectLiteralExcessProperties.ts(23,29): error TS2322: Type '{ couleur: string; }' is not assignable to type 'Cover | Cover[]'. Object literal may only specify known properties, and 'couleur' does not exist in type 'Cover | Cover[]'. tests/cases/compiler/objectLiteralExcessProperties.ts(25,27): error TS2322: Type '{ forewarned: string; }' is not assignable to type 'Book | Book[]'. Object literal may only specify known properties, and 'forewarned' does not exist in type 'Book | Book[]'. -tests/cases/compiler/objectLiteralExcessProperties.ts(33,27): error TS2322: Type '{ 0: { colour: string; }; }' is not assignable to type 'Indexed'. +tests/cases/compiler/objectLiteralExcessProperties.ts(33,5): error TS2322: Type '{ 0: { colour: string; }; }' is not assignable to type 'Indexed'. Property '0' is incompatible with index signature. - Type '{ colour: string; }' is not assignable to type 'Cover'. - Object literal may only specify known properties, but 'colour' does not exist in type 'Cover'. Did you mean to write 'color'? + Type '{ colour: string; }' has no properties in common with type 'Cover'. ==== tests/cases/compiler/objectLiteralExcessProperties.ts (10 errors) ==== @@ -68,9 +67,9 @@ tests/cases/compiler/objectLiteralExcessProperties.ts(33,27): error TS2322: Type !!! error TS2322: Object literal may only specify known properties, and 'price' does not exist in type 'Book & Cover'. var b7: Book & number = { foreword: "hi", price: 10.99 }; - ~~~~~~~~~~~~ + ~~ !!! error TS2322: Type '{ foreword: string; price: number; }' is not assignable to type 'Book & number'. -!!! error TS2322: Object literal may only specify known properties, and 'price' does not exist in type 'Book & number'. +!!! error TS2322: Type '{ foreword: string; price: number; }' is not assignable to type 'number'. var b8: Cover | Cover[] = { couleur : "non" }; ~~~~~~~~~~~~~~~ @@ -89,9 +88,8 @@ tests/cases/compiler/objectLiteralExcessProperties.ts(33,27): error TS2322: Type var b10: Indexed = { 0: { }, '1': { } }; // ok var b11: Indexed = { 0: { colour: "blue" } }; // nested object literal still errors - ~~~~~~~~~~~~~~ + ~~~ !!! error TS2322: Type '{ 0: { colour: string; }; }' is not assignable to type 'Indexed'. !!! error TS2322: Property '0' is incompatible with index signature. -!!! error TS2322: Type '{ colour: string; }' is not assignable to type 'Cover'. -!!! error TS2322: Object literal may only specify known properties, but 'colour' does not exist in type 'Cover'. Did you mean to write 'color'? +!!! error TS2322: Type '{ colour: string; }' has no properties in common with type 'Cover'. \ No newline at end of file diff --git a/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.errors.txt b/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.errors.txt index 1225f9ab2ce60..bab540204a4ed 100644 --- a/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.errors.txt +++ b/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.errors.txt @@ -11,6 +11,7 @@ tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring.ts(80,13): erro Type 'number' is not assignable to type 'string'. tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring.ts(85,8): error TS2322: Type '5' is not assignable to type 'string'. tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring.ts(85,8): error TS2322: Type 'number' is not assignable to type 'string'. +tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring.ts(85,19): error TS2322: Type '{ x: number; }' is not assignable to type '{ x: string; }'. tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring.ts(85,19): error TS2322: Type '{ x: number; }' is not assignable to type '{ x: string; }'. Types of property 'x' are incompatible. Type 'number' is not assignable to type 'string'. @@ -18,7 +19,7 @@ tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring.ts(111,12): err tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring.ts(111,14): error TS1312: '=' can only be used in an object literal property inside a destructuring assignment. -==== tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring.ts (14 errors) ==== +==== tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring.ts (15 errors) ==== (function() { var s0; for ({ s0 = 5 } of [{ s0: 1 }]) { @@ -129,6 +130,8 @@ tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring.ts(111,14): err ~~ !!! error TS2322: Type 'number' is not assignable to type 'string'. ~~ +!!! error TS2322: Type '{ x: number; }' is not assignable to type '{ x: string; }'. + ~~ !!! error TS2322: Type '{ x: number; }' is not assignable to type '{ x: string; }'. !!! error TS2322: Types of property 'x' are incompatible. !!! error TS2322: Type 'number' is not assignable to type 'string'. diff --git a/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring_ES6.errors.txt b/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring_ES6.errors.txt index acff36b6d1f3f..73b87b3e0c0c9 100644 --- a/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring_ES6.errors.txt +++ b/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring_ES6.errors.txt @@ -11,6 +11,7 @@ tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring_ES6.ts(80,13): Type 'number' is not assignable to type 'string'. tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring_ES6.ts(85,8): error TS2322: Type '5' is not assignable to type 'string'. tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring_ES6.ts(85,8): error TS2322: Type 'number' is not assignable to type 'string'. +tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring_ES6.ts(85,19): error TS2322: Type '{ x: number; }' is not assignable to type '{ x: string; }'. tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring_ES6.ts(85,19): error TS2322: Type '{ x: number; }' is not assignable to type '{ x: string; }'. Types of property 'x' are incompatible. Type 'number' is not assignable to type 'string'. @@ -18,7 +19,7 @@ tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring_ES6.ts(111,12): tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring_ES6.ts(111,14): error TS1312: '=' can only be used in an object literal property inside a destructuring assignment. -==== tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring_ES6.ts (14 errors) ==== +==== tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring_ES6.ts (15 errors) ==== (function() { var s0; for ({ s0 = 5 } of [{ s0: 1 }]) { @@ -129,6 +130,8 @@ tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring_ES6.ts(111,14): ~~ !!! error TS2322: Type 'number' is not assignable to type 'string'. ~~ +!!! error TS2322: Type '{ x: number; }' is not assignable to type '{ x: string; }'. + ~~ !!! error TS2322: Type '{ x: number; }' is not assignable to type '{ x: string; }'. !!! error TS2322: Types of property 'x' are incompatible. !!! error TS2322: Type 'number' is not assignable to type 'string'. diff --git a/tests/cases/compiler/nestedExcessPropertyChecks.ts b/tests/cases/compiler/nestedExcessPropertyChecks.ts new file mode 100644 index 0000000000000..6293e58c7d0f1 --- /dev/null +++ b/tests/cases/compiler/nestedExcessPropertyChecks.ts @@ -0,0 +1,103 @@ +// Repro #20863 + +// An object to hold all the possible options +type AllOptions = { + startDate: Date + endDate: Date + author: number +} + +// Any combination of startDate, endDate can be used +type DateOptions = + | Pick + | Pick + | Pick + +type AuthorOptions = Pick + +type AllowedOptions = DateOptions | AuthorOptions + +// options double dips +const options: AllowedOptions = { + startDate: new Date(), // error + author: 1 +} + +// Repro #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 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 +let f: D = {a: {x: 'hello', y: 2}, c: 5}; // y does not exist in type D + +// Repro #23706 + +type Template = { + [K in keyof T]: Template; +} & { + __name?: string; +} + +const template: Template<{ + foo: number; + bar: { + baz: string; + } +}> = { + foo: 1, + __name: 'n', + bar: { + baz: 'b', + not_exist_key: true // error + } +}; + +// Other tests + +// Empty alias normalises {} for intersection, but EmptyInterface will +// not. Check that they behave the same. + +type Empty = {}; +interface EmptyInterface {} + +let empty1: A & Empty = {x: "hello"}; +let empty2: A & EmptyInterface = {x: "hello"}; +let empty3: A & Empty = {x: "hello", y: true}; // error as A & Empty = A +let empty4: A & EmptyInterface = {x: "hello", y: true}; // error as A & EmptyInterface is equivalent to A +let empty5: {x: A} & {x: Empty} = {x: {x: "hello"}}; +let empty6: {x: A} & {x: EmptyInterface} = {x: {x: "hello"}}; +let empty7: {x: A} & {x: Empty} = {x: {x: "hello", y: true}}; // error +let empty8: {x: A} & {x: EmptyInterface} = {x: {x: "hello", y: true}}; // error + +// Nesting with intersection and union + +type Nesting = {x: {a: boolean, b: string}} | {x: {y: boolean} & {z: string} & {}}; + +let nesting1: Nesting = {x: {a: true, b: "b"}}; +let nesting2: Nesting = {y: "excess"}; // y is excess +let nesting3: Nesting = {x: {excess: true}}; // excess is excess +let nesting4: Nesting = {x: {a: true, b: "b", y: true, z: "hello"}}; // Double dipping on both sides of the union +let nesting5: Nesting = {x: {y: true, z: "b", excess: true}}; // excess is excess + + +type InnerUnion = {x: ({y: string} | {z: boolean})} & {x: {q: boolean}}; +let innerUnion1: InnerUnion = {x: {y: "ok", q: true}}; +let innerUnion2: InnerUnion = {x: {z: true, q: true}}; +let innerUnion3: InnerUnion = {x: {y: "not ok", z: true, q: true}};