diff --git a/packages/babel-generator/src/generators/flow.ts b/packages/babel-generator/src/generators/flow.ts index 133fec107f37..c307399b5e4c 100644 --- a/packages/babel-generator/src/generators/flow.ts +++ b/packages/babel-generator/src/generators/flow.ts @@ -462,6 +462,13 @@ export function ThisTypeAnnotation(this: Printer) { this.word("this"); } +export function IndexedAccessType(this: Printer, node: t.IndexedAccessType) { + this.print(node.objectType, node); + this.token("["); + this.print(node.indexType, node); + this.token("]"); +} + export function TupleTypeAnnotation( this: Printer, node: t.TupleTypeAnnotation, diff --git a/packages/babel-generator/test/fixtures/flow/indexed-access/input.js b/packages/babel-generator/test/fixtures/flow/indexed-access/input.js new file mode 100644 index 000000000000..8546f903ccc3 --- /dev/null +++ b/packages/babel-generator/test/fixtures/flow/indexed-access/input.js @@ -0,0 +1 @@ +type T = Obj[K]; diff --git a/packages/babel-generator/test/fixtures/flow/indexed-access/output.js b/packages/babel-generator/test/fixtures/flow/indexed-access/output.js new file mode 100644 index 000000000000..0ab89b7fef90 --- /dev/null +++ b/packages/babel-generator/test/fixtures/flow/indexed-access/output.js @@ -0,0 +1 @@ +type T = Obj[K]; \ 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 01bf25e2f351..7a4e1aaf86fd 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -1624,10 +1624,17 @@ 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.expect(tt.bracketR); + 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/test/fixtures/flow/indexed-access/basic/input.js b/packages/babel-parser/test/fixtures/flow/indexed-access/basic/input.js new file mode 100644 index 000000000000..9ec5c7fc5830 --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/indexed-access/basic/input.js @@ -0,0 +1 @@ +type A = Obj['a']; diff --git a/packages/babel-parser/test/fixtures/flow/indexed-access/basic/output.json b/packages/babel-parser/test/fixtures/flow/indexed-access/basic/output.json new file mode 100644 index 000000000000..9ec9d9363ec4 --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/indexed-access/basic/output.json @@ -0,0 +1,46 @@ +{ + "type": "File", + "start":0,"end":18,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":18}}, + "program": { + "type": "Program", + "start":0,"end":18,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":18}}, + "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" + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/flow/indexed-access/legacy-array-notation/input.js b/packages/babel-parser/test/fixtures/flow/indexed-access/legacy-array-notation/input.js new file mode 100644 index 000000000000..8c9a4842ef98 --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/indexed-access/legacy-array-notation/input.js @@ -0,0 +1 @@ +type E = Obj['bar'][]; diff --git a/packages/babel-parser/test/fixtures/flow/indexed-access/legacy-array-notation/output.json b/packages/babel-parser/test/fixtures/flow/indexed-access/legacy-array-notation/output.json new file mode 100644 index 000000000000..f98f23cf23c2 --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/indexed-access/legacy-array-notation/output.json @@ -0,0 +1,50 @@ +{ + "type": "File", + "start":0,"end":22,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":22}}, + "program": { + "type": "Program", + "start":0,"end":22,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":22}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "TypeAlias", + "start":0,"end":22,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":22}}, + "id": { + "type": "Identifier", + "start":5,"end":6,"loc":{"start":{"line":1,"column":5},"end":{"line":1,"column":6},"identifierName":"E"}, + "name": "E" + }, + "typeParameters": null, + "right": { + "type": "ArrayTypeAnnotation", + "start":9,"end":21,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":21}}, + "elementType": { + "type": "IndexedAccessType", + "start":9,"end":19,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":19}}, + "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":18,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":18}}, + "extra": { + "rawValue": "bar", + "raw": "'bar'" + }, + "value": "bar" + } + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/flow/indexed-access/nested/input.js b/packages/babel-parser/test/fixtures/flow/indexed-access/nested/input.js new file mode 100644 index 000000000000..36f39368736c --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/indexed-access/nested/input.js @@ -0,0 +1 @@ +type C = Obj['bar'][foo]['boz']; diff --git a/packages/babel-parser/test/fixtures/flow/indexed-access/nested/output.json b/packages/babel-parser/test/fixtures/flow/indexed-access/nested/output.json new file mode 100644 index 000000000000..f336be927b93 --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/indexed-access/nested/output.json @@ -0,0 +1,73 @@ +{ + "type": "File", + "start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":32}}, + "program": { + "type": "Program", + "start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":32}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "TypeAlias", + "start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":32}}, + "id": { + "type": "Identifier", + "start":5,"end":6,"loc":{"start":{"line":1,"column":5},"end":{"line":1,"column":6},"identifierName":"C"}, + "name": "C" + }, + "typeParameters": null, + "right": { + "type": "IndexedAccessType", + "start":9,"end":31,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":31}}, + "objectType": { + "type": "IndexedAccessType", + "start":9,"end":24,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":24}}, + "objectType": { + "type": "IndexedAccessType", + "start":9,"end":19,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":19}}, + "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":18,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":18}}, + "extra": { + "rawValue": "bar", + "raw": "'bar'" + }, + "value": "bar" + } + }, + "indexType": { + "type": "GenericTypeAnnotation", + "start":20,"end":23,"loc":{"start":{"line":1,"column":20},"end":{"line":1,"column":23}}, + "typeParameters": null, + "id": { + "type": "Identifier", + "start":20,"end":23,"loc":{"start":{"line":1,"column":20},"end":{"line":1,"column":23},"identifierName":"foo"}, + "name": "foo" + } + } + }, + "indexType": { + "type": "StringLiteralTypeAnnotation", + "start":25,"end":30,"loc":{"start":{"line":1,"column":25},"end":{"line":1,"column":30}}, + "extra": { + "rawValue": "boz", + "raw": "'boz'" + }, + "value": "boz" + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/flow/indexed-access/parenthesized/input.js b/packages/babel-parser/test/fixtures/flow/indexed-access/parenthesized/input.js new file mode 100644 index 000000000000..e85f1fb31688 --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/indexed-access/parenthesized/input.js @@ -0,0 +1 @@ +type D = (Obj['bar'])['baz']; diff --git a/packages/babel-parser/test/fixtures/flow/indexed-access/parenthesized/output.json b/packages/babel-parser/test/fixtures/flow/indexed-access/parenthesized/output.json new file mode 100644 index 000000000000..e81fcd870d43 --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/indexed-access/parenthesized/output.json @@ -0,0 +1,59 @@ +{ + "type": "File", + "start":0,"end":29,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":29}}, + "program": { + "type": "Program", + "start":0,"end":29,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":29}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "TypeAlias", + "start":0,"end":29,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":29}}, + "id": { + "type": "Identifier", + "start":5,"end":6,"loc":{"start":{"line":1,"column":5},"end":{"line":1,"column":6},"identifierName":"D"}, + "name": "D" + }, + "typeParameters": null, + "right": { + "type": "IndexedAccessType", + "start":9,"end":28,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":28}}, + "objectType": { + "type": "IndexedAccessType", + "start":10,"end":20,"loc":{"start":{"line":1,"column":10},"end":{"line":1,"column":20}}, + "objectType": { + "type": "GenericTypeAnnotation", + "start":10,"end":13,"loc":{"start":{"line":1,"column":10},"end":{"line":1,"column":13}}, + "typeParameters": null, + "id": { + "type": "Identifier", + "start":10,"end":13,"loc":{"start":{"line":1,"column":10},"end":{"line":1,"column":13},"identifierName":"Obj"}, + "name": "Obj" + } + }, + "indexType": { + "type": "StringLiteralTypeAnnotation", + "start":14,"end":19,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":19}}, + "extra": { + "rawValue": "bar", + "raw": "'bar'" + }, + "value": "bar" + } + }, + "indexType": { + "type": "StringLiteralTypeAnnotation", + "start":22,"end":27,"loc":{"start":{"line":1,"column":22},"end":{"line":1,"column":27}}, + "extra": { + "rawValue": "baz", + "raw": "'baz'" + }, + "value": "baz" + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/flow/indexed-access/type-params/input.js b/packages/babel-parser/test/fixtures/flow/indexed-access/type-params/input.js new file mode 100644 index 000000000000..4b97c060ac86 --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/indexed-access/type-params/input.js @@ -0,0 +1 @@ +type B = Array[number]; diff --git a/packages/babel-parser/test/fixtures/flow/indexed-access/type-params/output.json b/packages/babel-parser/test/fixtures/flow/indexed-access/type-params/output.json new file mode 100644 index 000000000000..53fa61b59bb8 --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/indexed-access/type-params/output.json @@ -0,0 +1,50 @@ +{ + "type": "File", + "start":0,"end":31,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":31}}, + "program": { + "type": "Program", + "start":0,"end":31,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":31}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "TypeAlias", + "start":0,"end":31,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":31}}, + "id": { + "type": "Identifier", + "start":5,"end":6,"loc":{"start":{"line":1,"column":5},"end":{"line":1,"column":6},"identifierName":"B"}, + "name": "B" + }, + "typeParameters": null, + "right": { + "type": "IndexedAccessType", + "start":9,"end":30,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":30}}, + "objectType": { + "type": "GenericTypeAnnotation", + "start":9,"end":22,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":22}}, + "typeParameters": { + "type": "TypeParameterInstantiation", + "start":14,"end":22,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":22}}, + "params": [ + { + "type": "StringTypeAnnotation", + "start":15,"end":21,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":21}} + } + ] + }, + "id": { + "type": "Identifier", + "start":9,"end":14,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":14},"identifierName":"Array"}, + "name": "Array" + } + }, + "indexType": { + "type": "NumberTypeAnnotation", + "start":23,"end":29,"loc":{"start":{"line":1,"column":23},"end":{"line":1,"column":29}} + } + } + } + ], + "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..f0b08dea980e 100755 --- a/packages/babel-types/src/asserts/generated/index.ts +++ b/packages/babel-types/src/asserts/generated/index.ts @@ -758,6 +758,12 @@ export function assertThisTypeAnnotation( ): asserts node is t.ThisTypeAnnotation { assert("ThisTypeAnnotation", node, opts); } +export function assertIndexedAccessType( + node: object | null | undefined, + opts?: object | null, +): asserts node is t.IndexedAccessType { + assert("IndexedAccessType", node, opts); +} export function assertTupleTypeAnnotation( 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 df363a9e857d..814549389eb3 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 @@ -1271,6 +1272,12 @@ export interface ThisTypeAnnotation extends BaseNode { type: "ThisTypeAnnotation"; } +export interface IndexedAccessType extends BaseNode { + type: "IndexedAccessType"; + objectType: FlowType; + indexType: FlowType; +} + export interface TupleTypeAnnotation extends BaseNode { type: "TupleTypeAnnotation"; types: Array; @@ -2338,6 +2345,7 @@ export type Flow = | StringTypeAnnotation | SymbolTypeAnnotation | ThisTypeAnnotation + | IndexedAccessType | TupleTypeAnnotation | TypeofTypeAnnotation | TypeAlias @@ -2370,6 +2378,7 @@ export type FlowType = | StringTypeAnnotation | SymbolTypeAnnotation | ThisTypeAnnotation + | IndexedAccessType | TupleTypeAnnotation | TypeofTypeAnnotation | UnionTypeAnnotation diff --git a/packages/babel-types/src/builders/generated/index.ts b/packages/babel-types/src/builders/generated/index.ts index 2ade18539370..c1aa66f0de5c 100755 --- a/packages/babel-types/src/builders/generated/index.ts +++ b/packages/babel-types/src/builders/generated/index.ts @@ -752,6 +752,12 @@ export function symbolTypeAnnotation(): t.SymbolTypeAnnotation { export function thisTypeAnnotation(): t.ThisTypeAnnotation { return builder("ThisTypeAnnotation", ...arguments); } +export function indexedAccessType( + objectType: t.FlowType, + indexType: t.FlowType, +): t.IndexedAccessType { + return builder("IndexedAccessType", ...arguments); +} export function tupleTypeAnnotation( types: Array, ): t.TupleTypeAnnotation { diff --git a/packages/babel-types/src/builders/generated/uppercase.js b/packages/babel-types/src/builders/generated/uppercase.js index 3e25736a38d1..564d62653f02 100755 --- a/packages/babel-types/src/builders/generated/uppercase.js +++ b/packages/babel-types/src/builders/generated/uppercase.js @@ -133,6 +133,7 @@ export { stringTypeAnnotation as StringTypeAnnotation, symbolTypeAnnotation as SymbolTypeAnnotation, thisTypeAnnotation as ThisTypeAnnotation, + indexedAccessType as IndexedAccessType, tupleTypeAnnotation as TupleTypeAnnotation, typeofTypeAnnotation as TypeofTypeAnnotation, typeAlias as TypeAlias, diff --git a/packages/babel-types/src/definitions/flow.ts b/packages/babel-types/src/definitions/flow.ts index 3a0a9cebcc15..65dbaad141b4 100644 --- a/packages/babel-types/src/definitions/flow.ts +++ b/packages/babel-types/src/definitions/flow.ts @@ -381,6 +381,15 @@ defineType("ThisTypeAnnotation", { aliases: ["Flow", "FlowType", "FlowBaseAnnotation"], }); +defineType("IndexedAccessType", { + aliases: ["Flow", "FlowType"], + visitor: ["objectType", "indexType"], + fields: { + objectType: validateType("FlowType"), + indexType: validateType("FlowType"), + }, +}); + defineType("TupleTypeAnnotation", { visitor: ["types"], aliases: ["Flow", "FlowType"], diff --git a/packages/babel-types/src/validators/generated/index.ts b/packages/babel-types/src/validators/generated/index.ts index d24f3f81bfbf..ec71a9e7f7c0 100755 --- a/packages/babel-types/src/validators/generated/index.ts +++ b/packages/babel-types/src/validators/generated/index.ts @@ -2113,6 +2113,23 @@ export function isThisTypeAnnotation( 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 isTupleTypeAnnotation( node: object | null | undefined, opts?: object | null, @@ -5074,6 +5091,7 @@ export function isFlow( "StringTypeAnnotation" === nodeType || "SymbolTypeAnnotation" === nodeType || "ThisTypeAnnotation" === nodeType || + "IndexedAccessType" === nodeType || "TupleTypeAnnotation" === nodeType || "TypeofTypeAnnotation" === nodeType || "TypeAlias" === nodeType || @@ -5123,6 +5141,7 @@ export function isFlowType( "StringTypeAnnotation" === nodeType || "SymbolTypeAnnotation" === nodeType || "ThisTypeAnnotation" === nodeType || + "IndexedAccessType" === nodeType || "TupleTypeAnnotation" === nodeType || "TypeofTypeAnnotation" === nodeType || "UnionTypeAnnotation" === nodeType ||