diff --git a/esprima.js b/esprima.js index bbf25350c81..ea77a4faefc 100644 --- a/esprima.js +++ b/esprima.js @@ -173,6 +173,7 @@ parseYieldExpression: true, parseAwaitExpression: true ObjectExpression: 'ObjectExpression', ObjectPattern: 'ObjectPattern', ObjectTypeAnnotation: 'ObjectTypeAnnotation', + ObjectTypeIndexer: 'ObjectTypeIndexer', OptionalParameter: 'OptionalParameter', ParametricTypeAnnotation: 'ParametricTypeAnnotation', ParametricallyTypedIdentifier: 'ParametricallyTypedIdentifier', @@ -282,7 +283,8 @@ parseYieldExpression: true, parseAwaitExpression: true EachNotAllowed: 'Each is not supported', InvalidXJSAttributeValue: 'XJS value should be either an expression or a quoted XJS text', ExpectedXJSClosingTag: 'Expected corresponding XJS closing tag for %0', - AdjacentXJSElements: 'Adjacent XJS elements must be wrapped in an enclosing tag' + AdjacentXJSElements: 'Adjacent XJS elements must be wrapped in an enclosing tag', + DuplicateIndexer: 'An interface or object type can only have a single indexer property.' }; // See also tools/generate-unicode-regex.py. @@ -1924,11 +1926,21 @@ parseYieldExpression: true, parseAwaitExpression: true }; }, - createObjectTypeAnnotation: function (properties, nullable) { + createObjectTypeAnnotation: function (properties, nullable, indexer) { return { type: Syntax.ObjectTypeAnnotation, properties: properties, - nullable: nullable + nullable: nullable, + indexer: indexer + }; + }, + + createObjectTypeIndexer: function (id, key, value) { + return { + type: Syntax.ObjectTypeIndexer, + id: id, + key: key, + value: value }; }, @@ -3861,24 +3873,50 @@ parseYieldExpression: true, parseAwaitExpression: true // 12.2 Variable Statement + function parseObjectTypeIndexer() { + var id, key, marker = markerCreate(), value; + + expect('['); + id = parseObjectPropertyKey(); + expect(':'); + key = parseTypeAnnotation(true); + expect(']'); + expect(':'); + value = parseTypeAnnotation(true); + + return markerApply(marker, delegate.createObjectTypeIndexer( + id, + key, + value + )); + } + function parseObjectTypeAnnotation(nullable) { - var isMethod, marker, properties = [], property, propertyKey, - propertyTypeAnnotation; + var indexer = null, isMethod, marker, properties = [], property, + propertyKey, propertyTypeAnnotation; expect('{'); while (!match('}')) { marker = markerCreate(); - propertyKey = parseObjectPropertyKey(); - isMethod = match('('); - propertyTypeAnnotation = parseTypeAnnotation(); - properties.push(markerApply(marker, delegate.createProperty( - 'init', - propertyKey, - propertyTypeAnnotation, - isMethod, - false - ))); + + if (match('[')) { + if (indexer) { + throwError({}, Messages.DuplicateIndexer); + } + indexer = parseObjectTypeIndexer(); + } else { + propertyKey = parseObjectPropertyKey(); + isMethod = match('('); + propertyTypeAnnotation = parseTypeAnnotation(); + properties.push(markerApply(marker, delegate.createProperty( + 'init', + propertyKey, + propertyTypeAnnotation, + isMethod, + false + ))); + } if (!match('}')) { if (match(',') || match(';')) { @@ -3891,7 +3929,11 @@ parseYieldExpression: true, parseAwaitExpression: true expect('}'); - return delegate.createObjectTypeAnnotation(properties, nullable); + return delegate.createObjectTypeAnnotation( + properties, + nullable, + indexer + ); } function parseVoidTypeAnnotation() { diff --git a/test/fbtest.js b/test/fbtest.js index 9123d569291..55408731110 100644 --- a/test/fbtest.js +++ b/test/fbtest.js @@ -4058,6 +4058,7 @@ var fbTestFixture = { type: 'ObjectTypeAnnotation', properties: [], nullable: false, + indexer: null, range: [14, 18], loc: { start: { line: 1, column: 14 }, @@ -4650,6 +4651,7 @@ var fbTestFixture = { } }], nullable: false, + indexer: null, range: [5, 23], loc: { start: { line: 1, column: 5 }, @@ -4677,6 +4679,148 @@ var fbTestFixture = { } }, + 'var a: {numVal: number, [indexer: string]: number};': { + type: 'VariableDeclaration', + declarations: [{ + type: 'VariableDeclarator', + id: { + type: 'TypeAnnotatedIdentifier', + id: { + type: 'Identifier', + name: 'a', + range: [4, 5], + loc: { + start: { line: 1, column: 4 }, + end: { line: 1, column: 5 } + } + }, + annotation: { + type: 'ObjectTypeAnnotation', + properties: [{ + type: 'Property', + key: { + type: 'Identifier', + name: 'numVal', + range: [8, 14], + loc: { + start: { line: 1, column: 8 }, + end: { line: 1, column: 14 } + } + }, + value: { + type: 'TypeAnnotation', + id: { + type: 'Identifier', + name: 'number', + range: [16, 22], + loc: { + start: { line: 1, column: 16 }, + end: { line: 1, column: 22 } + } + }, + params: null, + returnType: null, + nullable: false, + range: [14, 22], + loc: { + start: { line: 1, column: 14 }, + end: { line: 1, column: 22 } + } + }, + kind: 'init', + method: false, + shorthand: false, + range: [8, 22], + loc: { + start: { line: 1, column: 8 }, + end: { line: 1, column: 22 } + } + }], + nullable: false, + indexer: { + type: 'ObjectTypeIndexer', + id: { + type: 'Identifier', + name: 'indexer', + range: [25, 32], + loc: { + start: { line: 1, column: 25 }, + end: { line: 1, column: 32 } + } + }, + key: { + type: 'TypeAnnotation', + id: { + type: 'Identifier', + name: 'string', + range: [34, 40], + loc: { + start: { line: 1, column: 34 }, + end: { line: 1, column: 40 } + } + }, + params: null, + returnType: null, + nullable: false, + range: [34, 40], + loc: { + start: { line: 1, column: 34 }, + end: { line: 1, column: 40 } + } + }, + value: { + type: 'TypeAnnotation', + id: { + type: 'Identifier', + name: 'number', + range: [43, 49], + loc: { + start: { line: 1, column: 43 }, + end: { line: 1, column: 49 } + } + }, + params: null, + returnType: null, + nullable: false, + range: [43, 49], + loc: { + start: { line: 1, column: 43 }, + end: { line: 1, column: 49 } + } + }, + range: [24, 49], + loc: { + start: { line: 1, column: 24 }, + end: { line: 1, column: 49 } + } + }, + range: [5, 50], + loc: { + start: { line: 1, column: 5 }, + end: { line: 1, column: 50 } + } + }, + range: [4, 50], + loc: { + start: { line: 1, column: 4 }, + end: { line: 1, column: 50 } + } + }, + init: null, + range: [4, 50], + loc: { + start: { line: 1, column: 4 }, + end: { line: 1, column: 50 } + } + }], + kind: 'var', + range: [0, 51], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 51 } + } + }, + 'var a: ?{numVal: number};': { type: 'VariableDeclaration', declarations: [{ @@ -4735,6 +4879,7 @@ var fbTestFixture = { } }], nullable: true, + indexer: null, range: [5, 24], loc: { start: { line: 1, column: 5 }, @@ -4859,6 +5004,7 @@ var fbTestFixture = { } }], nullable: false, + indexer: null, range: [5, 39], loc: { start: { line: 1, column: 5 }, @@ -4957,6 +5103,7 @@ var fbTestFixture = { } }], nullable: false, + indexer: null, range: [14, 32], loc: { start: { line: 1, column: 14 }, @@ -4973,6 +5120,7 @@ var fbTestFixture = { } }], nullable: false, + indexer: null, range: [5, 33], loc: { start: { line: 1, column: 5 }, @@ -5071,6 +5219,7 @@ var fbTestFixture = { } }], nullable: true, + indexer: null, range: [14, 33], loc: { start: { line: 1, column: 14 }, @@ -5087,6 +5236,7 @@ var fbTestFixture = { } }], nullable: false, + indexer: null, range: [5, 34], loc: { start: { line: 1, column: 5 }, @@ -5211,6 +5361,7 @@ var fbTestFixture = { } }], nullable: false, + indexer: null, range: [5, 39], loc: { start: { line: 1, column: 5 }, @@ -6447,6 +6598,7 @@ var fbTestFixture = { } }], nullable: false, + indexer: null, range: [7, 22], loc: { start: { line: 1, column: 7 }, @@ -6590,6 +6742,7 @@ var fbTestFixture = { } }], nullable: false, + indexer: null, range: [7, 21], loc: { start: { line: 1, column: 7 }, @@ -6829,6 +6982,7 @@ var fbTestFixture = { } }], nullable: false, + indexer: null, range: [16, 32], loc: { start: { line: 1, column: 16 }, @@ -7248,6 +7402,14 @@ var fbTestFixture = { column: 17, message: 'Error: Line 1: Unexpected identifier', description: 'Unexpected identifier' + }, + + 'var a: { [a: number]: string; [b: number]: string; };': { + index: 29, + lineNumber: 1, + column: 30, + message: 'Error: Line 1: An interface or object type can only have a single indexer property.', + description: 'An interface or object type can only have a single indexer property.' } }, 'Type Alias': { @@ -7408,6 +7570,7 @@ var fbTestFixture = { body: { type: 'ObjectTypeAnnotation', properties: [], + indexer: null, range: [12, 14], loc: { start: { line: 1, column: 12 }, @@ -7446,6 +7609,7 @@ var fbTestFixture = { body: { type: 'ObjectTypeAnnotation', properties: [], + indexer: null, range: [22, 24], loc: { start: { line: 1, column: 22 }, @@ -7520,6 +7684,7 @@ var fbTestFixture = { body: { type: 'ObjectTypeAnnotation', properties: [], + indexer: null, range: [34, 36], loc: { start: { line: 1, column: 34 }, @@ -7680,6 +7845,7 @@ var fbTestFixture = { end: { line: 1, column: 31 } } }], + indexer: null, range: [12, 34], loc: { start: { line: 1, column: 12 }, @@ -7692,6 +7858,140 @@ var fbTestFixture = { start: { line: 1, column: 0 }, end: { line: 1, column: 34 } } + }, + "interface Dictionary { [index: string]: string; length: number; }": { + type: 'InterfaceDeclaration', + id: { + type: 'TypeAnnotation', + id: { + type: 'Identifier', + name: 'Dictionary', + range: [10, 20], + loc: { + start: { line: 1, column: 10 }, + end: { line: 1, column: 20 } + } + }, + params: null, + returnType: null, + nullable: false, + range: [10, 20], + loc: { + start: { line: 1, column: 10 }, + end: { line: 1, column: 20 } + } + }, + body: { + type: 'ObjectTypeAnnotation', + properties: [{ + type: 'Property', + key: { + type: 'Identifier', + name: 'length', + range: [48, 54], + loc: { + start: { line: 1, column: 48 }, + end: { line: 1, column: 54 } + } + }, + value: { + type: 'TypeAnnotation', + id: { + type: 'Identifier', + name: 'number', + range: [56, 62], + loc: { + start: { line: 1, column: 56 }, + end: { line: 1, column: 62 } + } + }, + params: null, + returnType: null, + nullable: false, + range: [54, 62], + loc: { + start: { line: 1, column: 54 }, + end: { line: 1, column: 62 } + } + }, + kind: 'init', + method: false, + shorthand: false, + range: [48, 62], + loc: { + start: { line: 1, column: 48 }, + end: { line: 1, column: 62 } + } + }], + indexer: { + type: 'ObjectTypeIndexer', + id: { + type: 'Identifier', + name: 'index', + range: [24, 29], + loc: { + start: { line: 1, column: 24 }, + end: { line: 1, column: 29 } + } + }, + key: { + type: 'TypeAnnotation', + id: { + type: 'Identifier', + name: 'string', + range: [31, 37], + loc: { + start: { line: 1, column: 31 }, + end: { line: 1, column: 37 } + } + }, + params: null, + returnType: null, + nullable: false, + range: [31, 37], + loc: { + start: { line: 1, column: 31 }, + end: { line: 1, column: 37 } + } + }, + value: { + type: 'TypeAnnotation', + id: { + type: 'Identifier', + name: 'string', + range: [40, 46], + loc: { + start: { line: 1, column: 40 }, + end: { line: 1, column: 46 } + } + }, + params: null, + returnType: null, + nullable: false, + range: [40, 46], + loc: { + start: { line: 1, column: 40 }, + end: { line: 1, column: 46 } + } + }, + range: [23, 46], + loc: { + start: { line: 1, column: 23 }, + end: { line: 1, column: 46 } + } + }, + range: [21, 65], + loc: { + start: { line: 1, column: 21 }, + end: { line: 1, column: 65 } + } + }, + 'extends': [], + range: [0, 65], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 65 } + } } }, 'Invalid Type Alias': { diff --git a/test/test.js b/test/test.js index 2473c50e932..435ce0d4c69 100644 --- a/test/test.js +++ b/test/test.js @@ -20035,6 +20035,7 @@ var testFixture = { ObjectExpression: 'ObjectExpression', ObjectPattern: 'ObjectPattern', ObjectTypeAnnotation: 'ObjectTypeAnnotation', + ObjectTypeIndexer: 'ObjectTypeIndexer', OptionalParameter: 'OptionalParameter', ParametricTypeAnnotation: 'ParametricTypeAnnotation', ParametricallyTypedIdentifier: 'ParametricallyTypedIdentifier',