Skip to content

Shorthand property #1127

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Nov 17, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ module ts {
break;
case SyntaxKind.Property:
case SyntaxKind.PropertyAssignment:
case SyntaxKind.ShorthandPropertyAssignment:
bindDeclaration(<Declaration>node, SymbolFlags.Property, SymbolFlags.PropertyExcludes, /*isBlockScopeContainer*/ false);
break;
case SyntaxKind.EnumMember:
Expand Down
35 changes: 31 additions & 4 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ module ts {
getReturnTypeOfSignature: getReturnTypeOfSignature,
getSymbolsInScope: getSymbolsInScope,
getSymbolInfo: getSymbolInfo,
getShorthandAssignmentValueSymbol: getShorthandAssignmentValueSymbol,
getTypeOfNode: getTypeOfNode,
typeToString: typeToString,
getSymbolDisplayBuilder: getSymbolDisplayBuilder,
Expand All @@ -109,6 +110,7 @@ module ts {
getAliasedSymbol: resolveImport,
isUndefinedSymbol: symbol => symbol === undefinedSymbol,
isArgumentsSymbol: symbol => symbol === argumentsSymbol,
hasEarlyErrors: hasEarlyErrors,
isEmitBlocked: isEmitBlocked
};

Expand Down Expand Up @@ -1665,6 +1667,13 @@ module ts {
}
return type;
}

// If it is a short-hand property assignment; Use the type of the identifier
if (declaration.kind === SyntaxKind.ShorthandPropertyAssignment) {
var type = checkIdentifier(<Identifier>declaration.name);
return type
}

// Rest parameters default to type any[], other parameters default to type any
var type = declaration.flags & NodeFlags.Rest ? createArrayType(anyType) : anyType;
checkImplicitAny(type);
Expand Down Expand Up @@ -2399,7 +2408,7 @@ module ts {
}

// Return the symbol for the property with the given name in the given type. Creates synthetic union properties when
// necessary, maps primtive types and type parameters are to their apparent types, and augments with properties from
// necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from
// Object and Function as appropriate.
function getPropertyOfType(type: Type, name: string): Symbol {
if (type.flags & TypeFlags.Union) {
Expand Down Expand Up @@ -2434,7 +2443,7 @@ module ts {
}

// Return the signatures of the given kind in the given type. Creates synthetic union signatures when necessary and
// maps primtive types and type parameters are to their apparent types.
// maps primitive types and type parameters are to their apparent types.
function getSignaturesOfType(type: Type, kind: SignatureKind): Signature[] {
return getSignaturesOfObjectOrUnionType(getApparentType(type), kind);
}
Expand All @@ -2447,7 +2456,7 @@ module ts {
}

// Return the index type of the given kind in the given type. Creates synthetic union index types when necessary and
// maps primtive types and type parameters are to their apparent types.
// maps primitive types and type parameters are to their apparent types.
function getIndexTypeOfType(type: Type, kind: IndexKind): Type {
return getIndexTypeOfObjectOrUnionType(getApparentType(type), kind);
}
Expand Down Expand Up @@ -5000,7 +5009,15 @@ module ts {
if (hasProperty(members, id)) {
var member = members[id];
if (member.flags & SymbolFlags.Property) {
var type = checkExpression((<PropertyDeclaration>member.declarations[0]).initializer, contextualMapper);
var memberDecl = <PropertyDeclaration>member.declarations[0];
var type: Type;
if (memberDecl.kind === SyntaxKind.PropertyAssignment) {
type = checkExpression(memberDecl.initializer, contextualMapper);
}
else {
Debug.assert(memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment);
type = checkExpression(memberDecl.name, contextualMapper);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assert here what type of member decl you think this is.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

}
var prop = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | member.flags, member.name);
prop.declarations = member.declarations;
prop.parent = member.parent;
Expand Down Expand Up @@ -8816,6 +8833,16 @@ module ts {
return undefined;
}

function getShorthandAssignmentValueSymbol(location: Node): Symbol {
// The function returns a value symbol of an identifier in the short-hand property assignment.
// This is necessary as an identifier in short-hand property assignment can contains two meaning:
// property name and property value.
if (location && location.kind === SyntaxKind.ShorthandPropertyAssignment) {
return resolveEntityName(location, (<ShortHandPropertyDeclaration>location).name, SymbolFlags.Value);
}
return undefined;
}

function getTypeOfNode(node: Node): Type {
if (isInsideWithStatementBody(node)) {
// We cannot answer semantic questions within a with block, do not proceed any further
Expand Down
1 change: 1 addition & 0 deletions src/compiler/diagnosticInformationMap.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ module ts {
let_declarations_can_only_be_declared_inside_a_block: { code: 1157, category: DiagnosticCategory.Error, key: "'let' declarations can only be declared inside a block." },
Invalid_template_literal_expected: { code: 1158, category: DiagnosticCategory.Error, key: "Invalid template literal; expected '}'" },
Tagged_templates_are_only_available_when_targeting_ECMAScript_6_and_higher: { code: 1159, category: DiagnosticCategory.Error, key: "Tagged templates are only available when targeting ECMAScript 6 and higher." },
A_object_member_cannot_be_declared_optional: { code: 1160, category: DiagnosticCategory.Error, key: "A object member cannot be declared optional." },
Duplicate_identifier_0: { code: 2300, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." },
Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor: { code: 2301, category: DiagnosticCategory.Error, key: "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor." },
Static_members_cannot_reference_class_type_parameters: { code: 2302, category: DiagnosticCategory.Error, key: "Static members cannot reference class type parameters." },
Expand Down
5 changes: 5 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,11 @@
"code": 1159
},

"A object member cannot be declared optional.": {
"category": "Error",
"code": 1160
},

"Duplicate identifier '{0}'.": {
"category": "Error",
"code": 2300
Expand Down
56 changes: 48 additions & 8 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -927,13 +927,14 @@ module ts {
}
}

function isNonExpressionIdentifier(node: Identifier) {
function isNotExpressionIdentifier(node: Identifier) {
var parent = node.parent;
switch (parent.kind) {
case SyntaxKind.Parameter:
case SyntaxKind.VariableDeclaration:
case SyntaxKind.Property:
case SyntaxKind.PropertyAssignment:
case SyntaxKind.ShorthandPropertyAssignment:
case SyntaxKind.EnumMember:
case SyntaxKind.Method:
case SyntaxKind.FunctionDeclaration:
Expand All @@ -957,17 +958,24 @@ module ts {
}
}

function emitIdentifier(node: Identifier) {
if (!isNonExpressionIdentifier(node)) {
var prefix = resolver.getExpressionNamePrefix(node);
if (prefix) {
write(prefix);
write(".");
}
function emitExpressionIdentifier(node: Identifier) {
var prefix = resolver.getExpressionNamePrefix(node);
if (prefix) {
write(prefix);
write(".");
}
write(getSourceTextOfLocalNode(node));
}

function emitIdentifier(node: Identifier) {
if (!isNotExpressionIdentifier(node)) {
emitExpressionIdentifier(node);
}
else {
write(getSourceTextOfLocalNode(node));
}
}

function emitThis(node: Node) {
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.LexicalThis) {
write("_this");
Expand Down Expand Up @@ -1033,6 +1041,36 @@ module ts {
emitTrailingComments(node);
}

function emitShortHandPropertyAssignment(node: ShortHandPropertyDeclaration) {
function emitAsNormalPropertyAssignment() {
emitLeadingComments(node);
// Emit identifier as an identifier
emit(node.name);
write(": ");
// Even though this is stored as identified because it is in short-hand property assignment,
// treated it as expression
emitExpressionIdentifier(node.name);
emitTrailingComments(node);
}

if (compilerOptions.target < ScriptTarget.ES6) {
emitAsNormalPropertyAssignment();
}
else if (compilerOptions.target >= ScriptTarget.ES6) {
// If short-hand property has a prefix, then regardless of the target version, we will emit it as normal property assignment
var prefix = resolver.getExpressionNamePrefix(node.name);
if (prefix) {
emitAsNormalPropertyAssignment();
}
// If short-hand property has no prefix, emit it as short-hand.
else {
emitLeadingComments(node);
emit(node.name);
emitTrailingComments(node);
}
}
}

function tryEmitConstantValue(node: PropertyAccess | IndexedAccess): boolean {
var constantValue = resolver.getConstantValue(node);
if (constantValue !== undefined) {
Expand Down Expand Up @@ -2250,6 +2288,8 @@ module ts {
return emitObjectLiteral(<ObjectLiteral>node);
case SyntaxKind.PropertyAssignment:
return emitPropertyAssignment(<PropertyDeclaration>node);
case SyntaxKind.ShorthandPropertyAssignment:
return emitShortHandPropertyAssignment(<ShortHandPropertyDeclaration>node);
case SyntaxKind.PropertyAccess:
return emitPropertyAccess(<PropertyAccess>node);
case SyntaxKind.IndexedAccess:
Expand Down
34 changes: 29 additions & 5 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ module ts {
child((<ParameterDeclaration>node).initializer);
case SyntaxKind.Property:
case SyntaxKind.PropertyAssignment:
case SyntaxKind.ShorthandPropertyAssignment:
return child((<PropertyDeclaration>node).name) ||
child((<PropertyDeclaration>node).type) ||
child((<PropertyDeclaration>node).initializer);
Expand Down Expand Up @@ -580,6 +581,7 @@ module ts {
case SyntaxKind.VariableDeclaration:
case SyntaxKind.Property:
case SyntaxKind.PropertyAssignment:
case SyntaxKind.ShorthandPropertyAssignment:
case SyntaxKind.EnumMember:
case SyntaxKind.Method:
case SyntaxKind.FunctionDeclaration:
Expand Down Expand Up @@ -2729,22 +2731,41 @@ module ts {
return finishNode(node);
}

function parsePropertyAssignment(): PropertyDeclaration {
var node = <PropertyDeclaration>createNode(SyntaxKind.PropertyAssignment);
node.name = parsePropertyName();
function parsePropertyAssignment(): Declaration {
var nodePos = scanner.getStartPos();
var nameToken = token;
var propertyName = parsePropertyName();
var node: Declaration;
if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) {
node = <PropertyDeclaration>createNode(SyntaxKind.PropertyAssignment, nodePos);
node.name = propertyName;
var sig = parseSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken, /* returnTokenRequired */ false);
var body = parseBody(/* ignoreMissingOpenBrace */ false);
// do not propagate property name as name for function expression
// for scenarios like
// var x = 1;
// var y = { x() { } }
// otherwise this will bring y.x into the scope of x which is incorrect
node.initializer = makeFunctionExpression(SyntaxKind.FunctionExpression, node.pos, undefined, sig, body);
(<PropertyDeclaration>node).initializer = makeFunctionExpression(SyntaxKind.FunctionExpression, node.pos, undefined, sig, body);
return finishNode(node);
}
// Disallow optional property assignment
if (token === SyntaxKind.QuestionToken) {
var questionStart = scanner.getTokenPos();
grammarErrorAtPos(questionStart, scanner.getStartPos() - questionStart, Diagnostics.A_object_member_cannot_be_declared_optional);
nextToken();
}

// Parse to check if it is short-hand property assignment or normal property assignment
if (token !== SyntaxKind.ColonToken && nameToken === SyntaxKind.Identifier) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks correct. just make sure you have tests for things like: var v = { class }. In this case, we do not have a shorthand property.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add that test

node = <ShortHandPropertyDeclaration>createNode(SyntaxKind.ShorthandPropertyAssignment, nodePos);
node.name = propertyName;
}
else {
node = <PropertyDeclaration>createNode(SyntaxKind.PropertyAssignment, nodePos);
node.name = propertyName;
parseExpected(SyntaxKind.ColonToken);
node.initializer = parseAssignmentExpression(false);
(<PropertyDeclaration>node).initializer = parseAssignmentExpression(false);
}
return finishNode(node);
}
Expand Down Expand Up @@ -2794,6 +2815,9 @@ module ts {
if (p.kind === SyntaxKind.PropertyAssignment) {
currentKind = Property;
}
else if (p.kind === SyntaxKind.ShorthandPropertyAssignment) {
currentKind = Property;
}
else if (p.kind === SyntaxKind.GetAccessor) {
currentKind = GetAccessor;
}
Expand Down
6 changes: 6 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ module ts {
ArrayLiteral,
ObjectLiteral,
PropertyAssignment,
ShorthandPropertyAssignment,
PropertyAccess,
IndexedAccess,
CallExpression,
Expand Down Expand Up @@ -335,6 +336,10 @@ module ts {
initializer?: Expression;
}

export interface ShortHandPropertyDeclaration extends Declaration {
name: Identifier;
}

export interface ParameterDeclaration extends VariableDeclaration { }

/**
Expand Down Expand Up @@ -719,6 +724,7 @@ module ts {
getReturnTypeOfSignature(signature: Signature): Type;
getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[];
getSymbolInfo(node: Node): Symbol;
getShorthandAssignmentValueSymbol(location: Node): Symbol;
getTypeOfNode(node: Node): Type;
typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string;
symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string;
Expand Down
Loading