Skip to content
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

Support parsing Flow's Optional Indexed Access Types #13224

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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 13 additions & 0 deletions packages/babel-generator/src/generators/flow.ts
Expand Up @@ -745,3 +745,16 @@ export function IndexedAccessType(this: Printer, node: t.IndexedAccessType) {
this.print(node.indexType, node);
this.token("]");
}

export function OptionalIndexedAccessType(
this: Printer,
node: t.OptionalIndexedAccessType,
) {
this.print(node.objectType, node);
if (node.optional) {
this.token("?.");
}
this.token("[");
this.print(node.indexType, node);
this.token("]");
}
4 changes: 4 additions & 0 deletions packages/babel-generator/src/node/parentheses.ts
Expand Up @@ -135,6 +135,10 @@ export function UnionTypeAnnotation(node: any, parent: any): boolean {

export { UnionTypeAnnotation as IntersectionTypeAnnotation };

export function OptionalIndexedAccessType(node: any, parent: any): boolean {
return t.isIndexedAccessType(parent, { objectType: node });
}

export function TSAsExpression() {
return true;
}
Expand Down
@@ -0,0 +1,9 @@
type A = Obj?.['a'];

type B = Array<string>?.[number];

type C = Obj?.['bar']['baz'];

type D = (Obj?.['bar'])['baz'];

type E = Obj?.['bar'][];
@@ -0,0 +1,5 @@
type A = Obj?.['a'];
type B = Array<string>?.[number];
type C = Obj?.['bar']['baz'];
type D = (Obj?.['bar'])['baz'];
type E = Obj?.['bar'][];
33 changes: 25 additions & 8 deletions packages/babel-parser/src/plugins/flow/index.js
Expand Up @@ -1619,24 +1619,38 @@ export default (superClass: Class<Parser>): Class<Parser> =>
}

flowParsePostfixType(): N.FlowTypeAnnotation {
const startPos = this.state.start,
startLoc = this.state.startLoc;
const startPos = this.state.start;
const startLoc = this.state.startLoc;
let type = this.flowParsePrimaryType();
while (this.match(tt.bracketL) && !this.canInsertSemicolon()) {
let seenOptionalIndexedAccess = false;
while (
(this.match(tt.bracketL) || this.match(tt.questionDot)) &&
!this.canInsertSemicolon()
) {
const node = this.startNodeAt(startPos, startLoc);
const optional = this.eat(tt.questionDot);
seenOptionalIndexedAccess = seenOptionalIndexedAccess || optional;
this.expect(tt.bracketL);
if (this.match(tt.bracketR)) {
if (!optional && 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<N.FlowIndexedAccessType>(
node,
"IndexedAccessType",
);
if (seenOptionalIndexedAccess) {
node.optional = optional;
type = this.finishNode<N.FlowOptionalIndexedAccessType>(
node,
"OptionalIndexedAccessType",
);
} else {
type = this.finishNode<N.FlowIndexedAccessType>(
node,
"IndexedAccessType",
);
}
}
}
return type;
Expand Down Expand Up @@ -2216,6 +2230,9 @@ export default (superClass: Class<Parser>): Class<Parser> =>
) {
return this.finishOp(tt.relational, 1);
} else if (this.state.inType && code === charCodes.questionMark) {
if (next === charCodes.dot) {
return this.finishOp(tt.questionDot, 2);
}
// allow double nullable types in Flow: ??string
return this.finishOp(tt.question, 1);
} else if (isIteratorStart(code, next)) {
Expand Down
7 changes: 7 additions & 0 deletions packages/babel-parser/src/types.js
Expand Up @@ -1058,6 +1058,13 @@ export type FlowIndexedAccessType = Node & {
indexType: FlowType,
};

export type FlowOptionalIndexedAccessType = Node & {
type: "OptionalIndexedAccessType",
objectType: FlowType,
indexType: FlowType,
optional: boolean,
};

// ESTree

export type EstreeProperty = NodeBase & {
Expand Down
@@ -0,0 +1,7 @@
type A = Obj?.['a'];

type B = Obj['a']?.['b'];

type C = Obj?.['a']['b'];

type D = Obj?.['a']?.['b'];
@@ -0,0 +1,190 @@
{
"type": "File",
"start":0,"end":103,"loc":{"start":{"line":1,"column":0},"end":{"line":7,"column":27}},
"program": {
"type": "Program",
"start":0,"end":103,"loc":{"start":{"line":1,"column":0},"end":{"line":7,"column":27}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "TypeAlias",
"start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}},
"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": "OptionalIndexedAccessType",
"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":15,"end":18,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":18}},
"extra": {
"rawValue": "a",
"raw": "'a'"
},
"value": "a"
},
"optional": true
}
},
{
"type": "TypeAlias",
"start":22,"end":47,"loc":{"start":{"line":3,"column":0},"end":{"line":3,"column":25}},
"id": {
"type": "Identifier",
"start":27,"end":28,"loc":{"start":{"line":3,"column":5},"end":{"line":3,"column":6},"identifierName":"B"},
"name": "B"
},
"typeParameters": null,
"right": {
"type": "OptionalIndexedAccessType",
"start":31,"end":46,"loc":{"start":{"line":3,"column":9},"end":{"line":3,"column":24}},
"objectType": {
"type": "IndexedAccessType",
"start":31,"end":39,"loc":{"start":{"line":3,"column":9},"end":{"line":3,"column":17}},
"objectType": {
"type": "GenericTypeAnnotation",
"start":31,"end":34,"loc":{"start":{"line":3,"column":9},"end":{"line":3,"column":12}},
"typeParameters": null,
"id": {
"type": "Identifier",
"start":31,"end":34,"loc":{"start":{"line":3,"column":9},"end":{"line":3,"column":12},"identifierName":"Obj"},
"name": "Obj"
}
},
"indexType": {
"type": "StringLiteralTypeAnnotation",
"start":35,"end":38,"loc":{"start":{"line":3,"column":13},"end":{"line":3,"column":16}},
"extra": {
"rawValue": "a",
"raw": "'a'"
},
"value": "a"
}
},
"indexType": {
"type": "StringLiteralTypeAnnotation",
"start":42,"end":45,"loc":{"start":{"line":3,"column":20},"end":{"line":3,"column":23}},
"extra": {
"rawValue": "b",
"raw": "'b'"
},
"value": "b"
},
"optional": true
}
},
{
"type": "TypeAlias",
"start":49,"end":74,"loc":{"start":{"line":5,"column":0},"end":{"line":5,"column":25}},
"id": {
"type": "Identifier",
"start":54,"end":55,"loc":{"start":{"line":5,"column":5},"end":{"line":5,"column":6},"identifierName":"C"},
"name": "C"
},
"typeParameters": null,
"right": {
"type": "OptionalIndexedAccessType",
"start":58,"end":73,"loc":{"start":{"line":5,"column":9},"end":{"line":5,"column":24}},
"objectType": {
"type": "OptionalIndexedAccessType",
"start":58,"end":68,"loc":{"start":{"line":5,"column":9},"end":{"line":5,"column":19}},
"objectType": {
"type": "GenericTypeAnnotation",
"start":58,"end":61,"loc":{"start":{"line":5,"column":9},"end":{"line":5,"column":12}},
"typeParameters": null,
"id": {
"type": "Identifier",
"start":58,"end":61,"loc":{"start":{"line":5,"column":9},"end":{"line":5,"column":12},"identifierName":"Obj"},
"name": "Obj"
}
},
"indexType": {
"type": "StringLiteralTypeAnnotation",
"start":64,"end":67,"loc":{"start":{"line":5,"column":15},"end":{"line":5,"column":18}},
"extra": {
"rawValue": "a",
"raw": "'a'"
},
"value": "a"
},
"optional": true
},
"indexType": {
"type": "StringLiteralTypeAnnotation",
"start":69,"end":72,"loc":{"start":{"line":5,"column":20},"end":{"line":5,"column":23}},
"extra": {
"rawValue": "b",
"raw": "'b'"
},
"value": "b"
},
"optional": false
}
},
{
"type": "TypeAlias",
"start":76,"end":103,"loc":{"start":{"line":7,"column":0},"end":{"line":7,"column":27}},
"id": {
"type": "Identifier",
"start":81,"end":82,"loc":{"start":{"line":7,"column":5},"end":{"line":7,"column":6},"identifierName":"D"},
"name": "D"
},
"typeParameters": null,
"right": {
"type": "OptionalIndexedAccessType",
"start":85,"end":102,"loc":{"start":{"line":7,"column":9},"end":{"line":7,"column":26}},
"objectType": {
"type": "OptionalIndexedAccessType",
"start":85,"end":95,"loc":{"start":{"line":7,"column":9},"end":{"line":7,"column":19}},
"objectType": {
"type": "GenericTypeAnnotation",
"start":85,"end":88,"loc":{"start":{"line":7,"column":9},"end":{"line":7,"column":12}},
"typeParameters": null,
"id": {
"type": "Identifier",
"start":85,"end":88,"loc":{"start":{"line":7,"column":9},"end":{"line":7,"column":12},"identifierName":"Obj"},
"name": "Obj"
}
},
"indexType": {
"type": "StringLiteralTypeAnnotation",
"start":91,"end":94,"loc":{"start":{"line":7,"column":15},"end":{"line":7,"column":18}},
"extra": {
"rawValue": "a",
"raw": "'a'"
},
"value": "a"
},
"optional": true
},
"indexType": {
"type": "StringLiteralTypeAnnotation",
"start":98,"end":101,"loc":{"start":{"line":7,"column":22},"end":{"line":7,"column":25}},
"extra": {
"rawValue": "b",
"raw": "'b'"
},
"value": "b"
},
"optional": true
}
}
],
"directives": []
}
}
3 changes: 3 additions & 0 deletions packages/babel-traverse/src/path/generated/asserts.ts
Expand Up @@ -390,6 +390,9 @@ export interface NodePathAssetions {
assertOptionalCallExpression(
opts?: object,
): asserts this is NodePath<t.OptionalCallExpression>;
assertOptionalIndexedAccessType(
opts?: object,
): asserts this is NodePath<t.OptionalIndexedAccessType>;
assertOptionalMemberExpression(
opts?: object,
): asserts this is NodePath<t.OptionalMemberExpression>;
Expand Down
3 changes: 3 additions & 0 deletions packages/babel-traverse/src/path/generated/validators.ts
Expand Up @@ -234,6 +234,9 @@ export interface NodePathValidators {
isOptionalCallExpression(
opts?: object,
): this is NodePath<t.OptionalCallExpression>;
isOptionalIndexedAccessType(
opts?: object,
): this is NodePath<t.OptionalIndexedAccessType>;
isOptionalMemberExpression(
opts?: object,
): this is NodePath<t.OptionalMemberExpression>;
Expand Down
6 changes: 6 additions & 0 deletions packages/babel-types/src/asserts/generated/index.ts
Expand Up @@ -884,6 +884,12 @@ export function assertIndexedAccessType(
): asserts node is t.IndexedAccessType {
assert("IndexedAccessType", node, opts);
}
export function assertOptionalIndexedAccessType(
node: object | null | undefined,
opts?: object | null,
): asserts node is t.OptionalIndexedAccessType {
assert("OptionalIndexedAccessType", node, opts);
}
export function assertJSXAttribute(
node: object | null | undefined,
opts?: object | null,
Expand Down
16 changes: 14 additions & 2 deletions packages/babel-types/src/ast-types/generated/index.ts
Expand Up @@ -208,6 +208,7 @@ export type Node =
| ObjectTypeSpreadProperty
| OpaqueType
| OptionalCallExpression
| OptionalIndexedAccessType
| OptionalMemberExpression
| ParenthesizedExpression
| Pattern
Expand Down Expand Up @@ -1395,6 +1396,13 @@ export interface IndexedAccessType extends BaseNode {
indexType: FlowType;
}

export interface OptionalIndexedAccessType extends BaseNode {
type: "OptionalIndexedAccessType";
objectType: FlowType;
indexType: FlowType;
optional: boolean;
}

export interface JSXAttribute extends BaseNode {
type: "JSXAttribute";
name: JSXIdentifier | JSXNamespacedName;
Expand Down Expand Up @@ -2361,7 +2369,9 @@ export type Flow =
| TypeParameterInstantiation
| UnionTypeAnnotation
| Variance
| VoidTypeAnnotation;
| VoidTypeAnnotation
| IndexedAccessType
| OptionalIndexedAccessType;
export type FlowType =
| AnyTypeAnnotation
| ArrayTypeAnnotation
Expand All @@ -2386,7 +2396,9 @@ export type FlowType =
| TupleTypeAnnotation
| TypeofTypeAnnotation
| UnionTypeAnnotation
| VoidTypeAnnotation;
| VoidTypeAnnotation
| IndexedAccessType
| OptionalIndexedAccessType;
export type FlowBaseAnnotation =
| AnyTypeAnnotation
| BooleanTypeAnnotation
Expand Down