Support parsing Flow's Indexed Access Types (#13053)

This commit is contained in:
Sosuke Suzuki 2021-04-07 18:15:46 +09:00 committed by Nicolò Ribaudo
parent eac0259ce2
commit f8aa32f767
15 changed files with 324 additions and 3 deletions

View File

@ -738,3 +738,10 @@ export function Variance(this: Printer, node: t.Variance) {
export function VoidTypeAnnotation(this: Printer) { export function VoidTypeAnnotation(this: Printer) {
this.word("void"); 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("]");
}

View File

@ -0,0 +1,9 @@
type A = Obj['a'];
type B = Array<string>[number];
type C = Obj['bar'][foo]['boz'];
type D = (Obj['bar'])['baz'];
type E = Obj['bar'][];

View File

@ -0,0 +1,5 @@
type A = Obj['a'];
type B = Array<string>[number];
type C = Obj['bar'][foo]['boz'];
type D = Obj['bar']['baz'];
type E = Obj['bar'][];

View File

@ -1623,10 +1623,20 @@ export default (superClass: Class<Parser>): Class<Parser> =>
let type = this.flowParsePrimaryType(); let type = this.flowParsePrimaryType();
while (this.match(tt.bracketL) && !this.canInsertSemicolon()) { while (this.match(tt.bracketL) && !this.canInsertSemicolon()) {
const node = this.startNodeAt(startPos, startLoc); const node = this.startNodeAt(startPos, startLoc);
node.elementType = type;
this.expect(tt.bracketL); this.expect(tt.bracketL);
this.expect(tt.bracketR); if (this.match(tt.bracketR)) {
node.elementType = type;
this.next(); // eat `]`
type = this.finishNode(node, "ArrayTypeAnnotation"); type = this.finishNode(node, "ArrayTypeAnnotation");
} else {
node.objectType = type;
node.indexType = this.flowParseType();
this.expect(tt.bracketR);
type = this.finishNode<N.FlowIndexedAccessType>(
node,
"IndexedAccessType",
);
}
} }
return type; return type;
} }

View File

@ -1050,6 +1050,12 @@ export type FlowInterfaceType = NodeBase & {
body: FlowObjectTypeAnnotation, body: FlowObjectTypeAnnotation,
}; };
export type FlowIndexedAccessType = Node & {
type: "IndexedAccessType",
objectType: FlowType,
indexType: FlowType,
};
// ESTree // ESTree
export type EstreeProperty = NodeBase & { export type EstreeProperty = NodeBase & {

View File

@ -0,0 +1,9 @@
type A = Obj['a'];
type B = Array<string>[number];
type C = Obj['bar'][foo]['boz'];
type D = (Obj['bar'])['baz'];
type E = Obj['bar'][];

View File

@ -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": []
}
}

View File

@ -257,6 +257,9 @@ export interface NodePathAssetions {
assertImportSpecifier( assertImportSpecifier(
opts?: object, opts?: object,
): asserts this is NodePath<t.ImportSpecifier>; ): asserts this is NodePath<t.ImportSpecifier>;
assertIndexedAccessType(
opts?: object,
): asserts this is NodePath<t.IndexedAccessType>;
assertInferredPredicate( assertInferredPredicate(
opts?: object, opts?: object,
): asserts this is NodePath<t.InferredPredicate>; ): asserts this is NodePath<t.InferredPredicate>;

View File

@ -149,6 +149,7 @@ export interface NodePathValidators {
opts?: object, opts?: object,
): this is NodePath<t.ImportNamespaceSpecifier>; ): this is NodePath<t.ImportNamespaceSpecifier>;
isImportSpecifier(opts?: object): this is NodePath<t.ImportSpecifier>; isImportSpecifier(opts?: object): this is NodePath<t.ImportSpecifier>;
isIndexedAccessType(opts?: object): this is NodePath<t.IndexedAccessType>;
isInferredPredicate(opts?: object): this is NodePath<t.InferredPredicate>; isInferredPredicate(opts?: object): this is NodePath<t.InferredPredicate>;
isInterfaceDeclaration( isInterfaceDeclaration(
opts?: object, opts?: object,

View File

@ -878,6 +878,12 @@ export function assertEnumDefaultedMember(
): asserts node is t.EnumDefaultedMember { ): asserts node is t.EnumDefaultedMember {
assert("EnumDefaultedMember", node, opts); 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( export function assertJSXAttribute(
node: object | null | undefined, node: object | null | undefined,
opts?: object | null, opts?: object | null,

View File

@ -151,6 +151,7 @@ export type Node =
| ImportDefaultSpecifier | ImportDefaultSpecifier
| ImportNamespaceSpecifier | ImportNamespaceSpecifier
| ImportSpecifier | ImportSpecifier
| IndexedAccessType
| InferredPredicate | InferredPredicate
| InterfaceDeclaration | InterfaceDeclaration
| InterfaceExtends | InterfaceExtends
@ -1387,6 +1388,12 @@ export interface EnumDefaultedMember extends BaseNode {
id: Identifier; id: Identifier;
} }
export interface IndexedAccessType extends BaseNode {
type: "IndexedAccessType";
objectType: FlowType;
indexType: FlowType;
}
export interface JSXAttribute extends BaseNode { export interface JSXAttribute extends BaseNode {
type: "JSXAttribute"; type: "JSXAttribute";
name: JSXIdentifier | JSXNamespacedName; name: JSXIdentifier | JSXNamespacedName;

View File

@ -854,6 +854,12 @@ export function enumStringMember(
export function enumDefaultedMember(id: t.Identifier): t.EnumDefaultedMember { export function enumDefaultedMember(id: t.Identifier): t.EnumDefaultedMember {
return builder("EnumDefaultedMember", ...arguments); return builder("EnumDefaultedMember", ...arguments);
} }
export function indexedAccessType(
objectType: t.FlowType,
indexType: t.FlowType,
): t.IndexedAccessType {
return builder("IndexedAccessType", ...arguments);
}
export function jsxAttribute( export function jsxAttribute(
name: t.JSXIdentifier | t.JSXNamespacedName, name: t.JSXIdentifier | t.JSXNamespacedName,
value?: value?:

View File

@ -153,6 +153,7 @@ export {
enumNumberMember as EnumNumberMember, enumNumberMember as EnumNumberMember,
enumStringMember as EnumStringMember, enumStringMember as EnumStringMember,
enumDefaultedMember as EnumDefaultedMember, enumDefaultedMember as EnumDefaultedMember,
indexedAccessType as IndexedAccessType,
jsxAttribute as JSXAttribute, jsxAttribute as JSXAttribute,
jsxClosingElement as JSXClosingElement, jsxClosingElement as JSXClosingElement,
jsxElement as JSXElement, jsxElement as JSXElement,

View File

@ -559,3 +559,11 @@ defineType("EnumDefaultedMember", {
id: validateType("Identifier"), id: validateType("Identifier"),
}, },
}); });
defineType("IndexedAccessType", {
visitor: ["objectType", "indexType"],
fields: {
objectType: validateType("FlowType"),
indexType: validateType("FlowType"),
},
});

View File

@ -2453,6 +2453,23 @@ export function isEnumDefaultedMember(
return false; 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( export function isJSXAttribute(
node: object | null | undefined, node: object | null | undefined,
opts?: object | null, opts?: object | null,