diff --git a/packages/babel-generator/src/generators/flow.ts b/packages/babel-generator/src/generators/flow.ts index 133fec107f37..b4373bffe3fe 100644 --- a/packages/babel-generator/src/generators/flow.ts +++ b/packages/babel-generator/src/generators/flow.ts @@ -738,3 +738,10 @@ export function Variance(this: Printer, node: t.Variance) { export function VoidTypeAnnotation(this: Printer) { this.word("void"); } + +export function IndexedAccessType(this: Printer, node: t.IndexedAccessType) { + this.print(node.objectType, node); + this.token("["); + this.print(node.indexType, node); + this.token("]"); +} diff --git a/packages/babel-generator/test/fixtures/flow/indexed-access-types/input.js b/packages/babel-generator/test/fixtures/flow/indexed-access-types/input.js new file mode 100644 index 000000000000..12a8ba2307c7 --- /dev/null +++ b/packages/babel-generator/test/fixtures/flow/indexed-access-types/input.js @@ -0,0 +1,9 @@ +type A = Obj['a']; + +type B = Array[number]; + +type C = Obj['bar'][foo]['boz']; + +type D = (Obj['bar'])['baz']; + +type E = Obj['bar'][]; diff --git a/packages/babel-generator/test/fixtures/flow/indexed-access-types/output.js b/packages/babel-generator/test/fixtures/flow/indexed-access-types/output.js new file mode 100644 index 000000000000..f1e611956ce9 --- /dev/null +++ b/packages/babel-generator/test/fixtures/flow/indexed-access-types/output.js @@ -0,0 +1,5 @@ +type A = Obj['a']; +type B = Array[number]; +type C = Obj['bar'][foo]['boz']; +type D = Obj['bar']['baz']; +type E = Obj['bar'][]; \ No newline at end of file diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index 7176a8e389dc..b94cc482cb11 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -1623,10 +1623,20 @@ export default (superClass: Class): Class => let type = this.flowParsePrimaryType(); while (this.match(tt.bracketL) && !this.canInsertSemicolon()) { const node = this.startNodeAt(startPos, startLoc); - node.elementType = type; this.expect(tt.bracketL); - this.expect(tt.bracketR); - type = this.finishNode(node, "ArrayTypeAnnotation"); + if (this.match(tt.bracketR)) { + node.elementType = type; + this.next(); // eat `]` + type = this.finishNode(node, "ArrayTypeAnnotation"); + } else { + node.objectType = type; + node.indexType = this.flowParseType(); + this.expect(tt.bracketR); + type = this.finishNode( + node, + "IndexedAccessType", + ); + } } return type; } diff --git a/packages/babel-parser/src/types.js b/packages/babel-parser/src/types.js index 018e3a39c90e..c85630767246 100644 --- a/packages/babel-parser/src/types.js +++ b/packages/babel-parser/src/types.js @@ -1050,6 +1050,12 @@ export type FlowInterfaceType = NodeBase & { body: FlowObjectTypeAnnotation, }; +export type FlowIndexedAccessType = Node & { + type: "IndexedAccessType", + objectType: FlowType, + indexType: FlowType, +}; + // ESTree export type EstreeProperty = NodeBase & { diff --git a/packages/babel-parser/test/fixtures/flow/indexed-access-types/1/input.js b/packages/babel-parser/test/fixtures/flow/indexed-access-types/1/input.js new file mode 100644 index 000000000000..12a8ba2307c7 --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/indexed-access-types/1/input.js @@ -0,0 +1,9 @@ +type A = Obj['a']; + +type B = Array[number]; + +type C = Obj['bar'][foo]['boz']; + +type D = (Obj['bar'])['baz']; + +type E = Obj['bar'][]; diff --git a/packages/babel-parser/test/fixtures/flow/indexed-access-types/1/output.json b/packages/babel-parser/test/fixtures/flow/indexed-access-types/1/output.json new file mode 100644 index 000000000000..49d9978d61f9 --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/indexed-access-types/1/output.json @@ -0,0 +1,226 @@ +{ + "type": "File", + "start":0,"end":140,"loc":{"start":{"line":1,"column":0},"end":{"line":9,"column":22}}, + "program": { + "type": "Program", + "start":0,"end":140,"loc":{"start":{"line":1,"column":0},"end":{"line":9,"column":22}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "TypeAlias", + "start":0,"end":18,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":18}}, + "id": { + "type": "Identifier", + "start":5,"end":6,"loc":{"start":{"line":1,"column":5},"end":{"line":1,"column":6},"identifierName":"A"}, + "name": "A" + }, + "typeParameters": null, + "right": { + "type": "IndexedAccessType", + "start":9,"end":17,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":17}}, + "objectType": { + "type": "GenericTypeAnnotation", + "start":9,"end":12,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":12}}, + "typeParameters": null, + "id": { + "type": "Identifier", + "start":9,"end":12,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":12},"identifierName":"Obj"}, + "name": "Obj" + } + }, + "indexType": { + "type": "StringLiteralTypeAnnotation", + "start":13,"end":16,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":16}}, + "extra": { + "rawValue": "a", + "raw": "'a'" + }, + "value": "a" + } + } + }, + { + "type": "TypeAlias", + "start":20,"end":51,"loc":{"start":{"line":3,"column":0},"end":{"line":3,"column":31}}, + "id": { + "type": "Identifier", + "start":25,"end":26,"loc":{"start":{"line":3,"column":5},"end":{"line":3,"column":6},"identifierName":"B"}, + "name": "B" + }, + "typeParameters": null, + "right": { + "type": "IndexedAccessType", + "start":29,"end":50,"loc":{"start":{"line":3,"column":9},"end":{"line":3,"column":30}}, + "objectType": { + "type": "GenericTypeAnnotation", + "start":29,"end":42,"loc":{"start":{"line":3,"column":9},"end":{"line":3,"column":22}}, + "typeParameters": { + "type": "TypeParameterInstantiation", + "start":34,"end":42,"loc":{"start":{"line":3,"column":14},"end":{"line":3,"column":22}}, + "params": [ + { + "type": "StringTypeAnnotation", + "start":35,"end":41,"loc":{"start":{"line":3,"column":15},"end":{"line":3,"column":21}} + } + ] + }, + "id": { + "type": "Identifier", + "start":29,"end":34,"loc":{"start":{"line":3,"column":9},"end":{"line":3,"column":14},"identifierName":"Array"}, + "name": "Array" + } + }, + "indexType": { + "type": "NumberTypeAnnotation", + "start":43,"end":49,"loc":{"start":{"line":3,"column":23},"end":{"line":3,"column":29}} + } + } + }, + { + "type": "TypeAlias", + "start":53,"end":85,"loc":{"start":{"line":5,"column":0},"end":{"line":5,"column":32}}, + "id": { + "type": "Identifier", + "start":58,"end":59,"loc":{"start":{"line":5,"column":5},"end":{"line":5,"column":6},"identifierName":"C"}, + "name": "C" + }, + "typeParameters": null, + "right": { + "type": "IndexedAccessType", + "start":62,"end":84,"loc":{"start":{"line":5,"column":9},"end":{"line":5,"column":31}}, + "objectType": { + "type": "IndexedAccessType", + "start":62,"end":77,"loc":{"start":{"line":5,"column":9},"end":{"line":5,"column":24}}, + "objectType": { + "type": "IndexedAccessType", + "start":62,"end":72,"loc":{"start":{"line":5,"column":9},"end":{"line":5,"column":19}}, + "objectType": { + "type": "GenericTypeAnnotation", + "start":62,"end":65,"loc":{"start":{"line":5,"column":9},"end":{"line":5,"column":12}}, + "typeParameters": null, + "id": { + "type": "Identifier", + "start":62,"end":65,"loc":{"start":{"line":5,"column":9},"end":{"line":5,"column":12},"identifierName":"Obj"}, + "name": "Obj" + } + }, + "indexType": { + "type": "StringLiteralTypeAnnotation", + "start":66,"end":71,"loc":{"start":{"line":5,"column":13},"end":{"line":5,"column":18}}, + "extra": { + "rawValue": "bar", + "raw": "'bar'" + }, + "value": "bar" + } + }, + "indexType": { + "type": "GenericTypeAnnotation", + "start":73,"end":76,"loc":{"start":{"line":5,"column":20},"end":{"line":5,"column":23}}, + "typeParameters": null, + "id": { + "type": "Identifier", + "start":73,"end":76,"loc":{"start":{"line":5,"column":20},"end":{"line":5,"column":23},"identifierName":"foo"}, + "name": "foo" + } + } + }, + "indexType": { + "type": "StringLiteralTypeAnnotation", + "start":78,"end":83,"loc":{"start":{"line":5,"column":25},"end":{"line":5,"column":30}}, + "extra": { + "rawValue": "boz", + "raw": "'boz'" + }, + "value": "boz" + } + } + }, + { + "type": "TypeAlias", + "start":87,"end":116,"loc":{"start":{"line":7,"column":0},"end":{"line":7,"column":29}}, + "id": { + "type": "Identifier", + "start":92,"end":93,"loc":{"start":{"line":7,"column":5},"end":{"line":7,"column":6},"identifierName":"D"}, + "name": "D" + }, + "typeParameters": null, + "right": { + "type": "IndexedAccessType", + "start":96,"end":115,"loc":{"start":{"line":7,"column":9},"end":{"line":7,"column":28}}, + "objectType": { + "type": "IndexedAccessType", + "start":97,"end":107,"loc":{"start":{"line":7,"column":10},"end":{"line":7,"column":20}}, + "objectType": { + "type": "GenericTypeAnnotation", + "start":97,"end":100,"loc":{"start":{"line":7,"column":10},"end":{"line":7,"column":13}}, + "typeParameters": null, + "id": { + "type": "Identifier", + "start":97,"end":100,"loc":{"start":{"line":7,"column":10},"end":{"line":7,"column":13},"identifierName":"Obj"}, + "name": "Obj" + } + }, + "indexType": { + "type": "StringLiteralTypeAnnotation", + "start":101,"end":106,"loc":{"start":{"line":7,"column":14},"end":{"line":7,"column":19}}, + "extra": { + "rawValue": "bar", + "raw": "'bar'" + }, + "value": "bar" + } + }, + "indexType": { + "type": "StringLiteralTypeAnnotation", + "start":109,"end":114,"loc":{"start":{"line":7,"column":22},"end":{"line":7,"column":27}}, + "extra": { + "rawValue": "baz", + "raw": "'baz'" + }, + "value": "baz" + } + } + }, + { + "type": "TypeAlias", + "start":118,"end":140,"loc":{"start":{"line":9,"column":0},"end":{"line":9,"column":22}}, + "id": { + "type": "Identifier", + "start":123,"end":124,"loc":{"start":{"line":9,"column":5},"end":{"line":9,"column":6},"identifierName":"E"}, + "name": "E" + }, + "typeParameters": null, + "right": { + "type": "ArrayTypeAnnotation", + "start":127,"end":139,"loc":{"start":{"line":9,"column":9},"end":{"line":9,"column":21}}, + "elementType": { + "type": "IndexedAccessType", + "start":127,"end":137,"loc":{"start":{"line":9,"column":9},"end":{"line":9,"column":19}}, + "objectType": { + "type": "GenericTypeAnnotation", + "start":127,"end":130,"loc":{"start":{"line":9,"column":9},"end":{"line":9,"column":12}}, + "typeParameters": null, + "id": { + "type": "Identifier", + "start":127,"end":130,"loc":{"start":{"line":9,"column":9},"end":{"line":9,"column":12},"identifierName":"Obj"}, + "name": "Obj" + } + }, + "indexType": { + "type": "StringLiteralTypeAnnotation", + "start":131,"end":136,"loc":{"start":{"line":9,"column":13},"end":{"line":9,"column":18}}, + "extra": { + "rawValue": "bar", + "raw": "'bar'" + }, + "value": "bar" + } + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-traverse/src/path/generated/asserts.ts b/packages/babel-traverse/src/path/generated/asserts.ts index b0e6e22ccf05..ac9c5bd4895e 100755 --- a/packages/babel-traverse/src/path/generated/asserts.ts +++ b/packages/babel-traverse/src/path/generated/asserts.ts @@ -257,6 +257,9 @@ export interface NodePathAssetions { assertImportSpecifier( opts?: object, ): asserts this is NodePath; + assertIndexedAccessType( + opts?: object, + ): asserts this is NodePath; assertInferredPredicate( opts?: object, ): asserts this is NodePath; diff --git a/packages/babel-traverse/src/path/generated/validators.ts b/packages/babel-traverse/src/path/generated/validators.ts index ced5e539075a..271f5e5595fa 100755 --- a/packages/babel-traverse/src/path/generated/validators.ts +++ b/packages/babel-traverse/src/path/generated/validators.ts @@ -149,6 +149,7 @@ export interface NodePathValidators { opts?: object, ): this is NodePath; isImportSpecifier(opts?: object): this is NodePath; + isIndexedAccessType(opts?: object): this is NodePath; isInferredPredicate(opts?: object): this is NodePath; isInterfaceDeclaration( opts?: object, diff --git a/packages/babel-types/src/asserts/generated/index.ts b/packages/babel-types/src/asserts/generated/index.ts index 04cf3195f184..ea97792a1e1c 100755 --- a/packages/babel-types/src/asserts/generated/index.ts +++ b/packages/babel-types/src/asserts/generated/index.ts @@ -878,6 +878,12 @@ export function assertEnumDefaultedMember( ): asserts node is t.EnumDefaultedMember { assert("EnumDefaultedMember", node, opts); } +export function assertIndexedAccessType( + node: object | null | undefined, + opts?: object | null, +): asserts node is t.IndexedAccessType { + assert("IndexedAccessType", node, opts); +} export function assertJSXAttribute( node: object | null | undefined, opts?: object | null, diff --git a/packages/babel-types/src/ast-types/generated/index.ts b/packages/babel-types/src/ast-types/generated/index.ts index 1938f1feca13..80b653c30440 100755 --- a/packages/babel-types/src/ast-types/generated/index.ts +++ b/packages/babel-types/src/ast-types/generated/index.ts @@ -151,6 +151,7 @@ export type Node = | ImportDefaultSpecifier | ImportNamespaceSpecifier | ImportSpecifier + | IndexedAccessType | InferredPredicate | InterfaceDeclaration | InterfaceExtends @@ -1387,6 +1388,12 @@ export interface EnumDefaultedMember extends BaseNode { id: Identifier; } +export interface IndexedAccessType extends BaseNode { + type: "IndexedAccessType"; + objectType: FlowType; + indexType: FlowType; +} + export interface JSXAttribute extends BaseNode { type: "JSXAttribute"; name: JSXIdentifier | JSXNamespacedName; diff --git a/packages/babel-types/src/builders/generated/index.ts b/packages/babel-types/src/builders/generated/index.ts index 2ade18539370..a0adb832b1cc 100755 --- a/packages/babel-types/src/builders/generated/index.ts +++ b/packages/babel-types/src/builders/generated/index.ts @@ -854,6 +854,12 @@ export function enumStringMember( export function enumDefaultedMember(id: t.Identifier): t.EnumDefaultedMember { return builder("EnumDefaultedMember", ...arguments); } +export function indexedAccessType( + objectType: t.FlowType, + indexType: t.FlowType, +): t.IndexedAccessType { + return builder("IndexedAccessType", ...arguments); +} export function jsxAttribute( name: t.JSXIdentifier | t.JSXNamespacedName, value?: diff --git a/packages/babel-types/src/builders/generated/uppercase.js b/packages/babel-types/src/builders/generated/uppercase.js index 3e25736a38d1..464b86423ce3 100755 --- a/packages/babel-types/src/builders/generated/uppercase.js +++ b/packages/babel-types/src/builders/generated/uppercase.js @@ -153,6 +153,7 @@ export { enumNumberMember as EnumNumberMember, enumStringMember as EnumStringMember, enumDefaultedMember as EnumDefaultedMember, + indexedAccessType as IndexedAccessType, jsxAttribute as JSXAttribute, jsxClosingElement as JSXClosingElement, jsxElement as JSXElement, diff --git a/packages/babel-types/src/definitions/flow.ts b/packages/babel-types/src/definitions/flow.ts index 3a0a9cebcc15..91ce3405a811 100644 --- a/packages/babel-types/src/definitions/flow.ts +++ b/packages/babel-types/src/definitions/flow.ts @@ -559,3 +559,11 @@ defineType("EnumDefaultedMember", { id: validateType("Identifier"), }, }); + +defineType("IndexedAccessType", { + visitor: ["objectType", "indexType"], + fields: { + objectType: validateType("FlowType"), + indexType: validateType("FlowType"), + }, +}); diff --git a/packages/babel-types/src/validators/generated/index.ts b/packages/babel-types/src/validators/generated/index.ts index d24f3f81bfbf..e86131e2182b 100755 --- a/packages/babel-types/src/validators/generated/index.ts +++ b/packages/babel-types/src/validators/generated/index.ts @@ -2453,6 +2453,23 @@ export function isEnumDefaultedMember( return false; } +export function isIndexedAccessType( + node: object | null | undefined, + opts?: object | null, +): node is t.IndexedAccessType { + if (!node) return false; + + const nodeType = (node as t.Node).type; + if (nodeType === "IndexedAccessType") { + if (typeof opts === "undefined") { + return true; + } else { + return shallowEqual(node, opts); + } + } + + return false; +} export function isJSXAttribute( node: object | null | undefined, opts?: object | null,