Skip to content

Commit bb81797

Browse files
committed
Check for excess properties
1 parent e40b86f commit bb81797

File tree

1 file changed

+30
-19
lines changed

1 file changed

+30
-19
lines changed

src/compiler/checker.ts

+30-19
Original file line numberDiff line numberDiff line change
@@ -7001,6 +7001,11 @@ namespace ts {
70017001
return checkIteratedTypeOrElementType(arrayOrIterableType, node.expression, /*allowStringInput*/ false);
70027002
}
70037003

7004+
function hasDefaultValue(node: BindingElement | Expression): boolean {
7005+
return (node.kind === SyntaxKind.BindingElement && !!(<BindingElement>node).initializer) ||
7006+
(node.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>node).operatorToken.kind === SyntaxKind.EqualsToken);
7007+
}
7008+
70047009
function checkArrayLiteral(node: ArrayLiteralExpression, contextualMapper?: TypeMapper): Type {
70057010
let elements = node.elements;
70067011
let hasSpreadElement = false;
@@ -7044,21 +7049,12 @@ namespace ts {
70447049
let contextualType = getContextualType(node);
70457050
if (contextualType && contextualTypeIsTupleLikeType(contextualType)) {
70467051
let pattern = contextualType.pattern;
7047-
// If array literal is contextually typed by a binding pattern or an assignment pattern,
7048-
// pad the resulting tuple type to make the lengths equal.
7049-
if (pattern && pattern.kind === SyntaxKind.ArrayBindingPattern) {
7050-
let bindingElements = (<BindingPattern>pattern).elements;
7051-
for (let i = elementTypes.length; i < bindingElements.length; i++) {
7052-
let hasDefaultValue = bindingElements[i].initializer;
7053-
elementTypes.push(hasDefaultValue ? (<TupleType>contextualType).elementTypes[i] : undefinedType);
7054-
}
7055-
}
7056-
else if (pattern && pattern.kind === SyntaxKind.ArrayLiteralExpression) {
7057-
let assignmentElements = (<ArrayLiteralExpression>pattern).elements;
7058-
for (let i = elementTypes.length; i < assignmentElements.length; i++) {
7059-
let hasDefaultValue = assignmentElements[i].kind === SyntaxKind.BinaryExpression &&
7060-
(<BinaryExpression>assignmentElements[i]).operatorToken.kind === SyntaxKind.EqualsToken;
7061-
elementTypes.push(hasDefaultValue ? (<TupleType>contextualType).elementTypes[i] : undefinedType);
7052+
// If array literal is contextually typed by a binding pattern or an assignment pattern, pad the
7053+
// resulting tuple type to make the lengths equal.
7054+
if (pattern && (pattern.kind === SyntaxKind.ArrayBindingPattern || pattern.kind === SyntaxKind.ArrayLiteralExpression)) {
7055+
let patternElements = (<BindingPattern | ArrayLiteralExpression>pattern).elements;
7056+
for (let i = elementTypes.length; i < patternElements.length; i++) {
7057+
elementTypes.push(hasDefaultValue(patternElements[i]) ? (<TupleType>contextualType).elementTypes[i] : undefinedType);
70627058
}
70637059
}
70647060
if (elementTypes.length) {
@@ -7134,7 +7130,8 @@ namespace ts {
71347130
let propertiesArray: Symbol[] = [];
71357131
let contextualType = getContextualType(node);
71367132
let contextualTypeHasPattern = contextualType && contextualType.pattern &&
7137-
contextualType.pattern.kind === SyntaxKind.ObjectBindingPattern;
7133+
(contextualType.pattern.kind === SyntaxKind.ObjectBindingPattern || contextualType.pattern.kind === SyntaxKind.ObjectLiteralExpression);
7134+
let inDestructuringPattern = isAssignmentTarget(node);
71387135
let typeFlags: TypeFlags = 0;
71397136

71407137
for (let memberDecl of node.properties) {
@@ -7155,13 +7152,24 @@ namespace ts {
71557152
}
71567153
typeFlags |= type.flags;
71577154
let prop = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | member.flags, member.name);
7158-
// If object literal is contextually typed by the implied type of a binding pattern, and if the
7159-
// binding pattern specifies a default value for the property, make the property optional.
7160-
if (contextualTypeHasPattern) {
7155+
if (inDestructuringPattern) {
7156+
// If object literal is an assignment pattern and if the assignment pattern specifies a default value
7157+
// for the property, make the property optional.
7158+
if (memberDecl.kind === SyntaxKind.PropertyAssignment && hasDefaultValue((<PropertyAssignment>memberDecl).initializer)) {
7159+
prop.flags |= SymbolFlags.Optional;
7160+
}
7161+
}
7162+
else if (contextualTypeHasPattern) {
7163+
// If object literal is contextually typed by the implied type of a binding pattern, and if the
7164+
// binding pattern specifies a default value for the property, make the property optional.
71617165
let impliedProp = getPropertyOfType(contextualType, member.name);
71627166
if (impliedProp) {
71637167
prop.flags |= impliedProp.flags & SymbolFlags.Optional;
71647168
}
7169+
else if (!compilerOptions.suppressExcessPropertyErrors) {
7170+
error(memberDecl.name, Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1,
7171+
symbolToString(member), typeToString(contextualType));
7172+
}
71657173
}
71667174
prop.declarations = member.declarations;
71677175
prop.parent = member.parent;
@@ -7205,6 +7213,9 @@ namespace ts {
72057213
let result = createAnonymousType(node.symbol, propertiesTable, emptyArray, emptyArray, stringIndexType, numberIndexType);
72067214
let freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : TypeFlags.FreshObjectLiteral;
72077215
result.flags |= TypeFlags.ObjectLiteral | TypeFlags.ContainsObjectLiteral | freshObjectLiteralFlag | (typeFlags & TypeFlags.PropagatingFlags);
7216+
if (inDestructuringPattern) {
7217+
result.pattern = node;
7218+
}
72087219
return result;
72097220

72107221
function getIndexType(kind: IndexKind) {

0 commit comments

Comments
 (0)