Skip to content

Commit

Permalink
[ts] Allow keywords in tuple labels (#15423)
Browse files Browse the repository at this point in the history
Co-authored-by: Nicol貌 Ribaudo <nicolo.ribaudo@gmail.com>
  • Loading branch information
Harpica and nicolo-ribaudo committed Mar 14, 2023
1 parent 98c6d63 commit 80863c2
Show file tree
Hide file tree
Showing 9 changed files with 331 additions and 20 deletions.
78 changes: 61 additions & 17 deletions packages/babel-parser/src/plugins/typescript/index.ts
Expand Up @@ -214,6 +214,8 @@ const TSErrors = ParseErrorEnum`typescript`({
`Single type parameter ${typeParameterName} should have a trailing comma. Example usage: <${typeParameterName},>.`,
StaticBlockCannotHaveModifier:
"Static class blocks cannot have any modifier.",
TupleOptionalAfterType:
"A labeled tuple optional element must be declared using a question mark after the name and before the colon (`name?: type`), rather than after the type (`name: type?`).",
TypeAnnotationAfterAssign:
"Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`.",
TypeImportCannotSpecifyDefaultAndNamed:
Expand Down Expand Up @@ -460,8 +462,6 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
case "TypeParametersOrArguments":
return this.match(tt.gt);
}

throw new Error("Unreachable");
}

tsParseList<T extends N.Node>(
Expand Down Expand Up @@ -1099,34 +1099,78 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
return this.finishNode(node, "TSTupleType");
}

tsParseTupleElementType(): N.TsType | N.TsNamedTupleMember {
tsParseTupleElementType(): N.TsNamedTupleMember | N.TsType {
// parses `...TsType[]`

const { startLoc } = this.state;

const rest = this.eat(tt.ellipsis);
let type: N.TsType | N.TsNamedTupleMember = this.tsParseType();
const optional = this.eat(tt.question);
const labeled = this.eat(tt.colon);

if (labeled) {
const labeledNode = this.startNodeAtNode<N.TsNamedTupleMember>(type);
labeledNode.optional = optional;
let labeled: boolean;
let label: N.Identifier;
let optional: boolean;
let type: N.TsNamedTupleMember | N.TsType;

const isWord = tokenIsKeywordOrIdentifier(this.state.type);
const chAfterWord = isWord ? this.lookaheadCharCode() : null;
if (chAfterWord === charCodes.colon) {
labeled = true;
optional = false;
label = this.parseIdentifier(true);
this.expect(tt.colon);
type = this.tsParseType();
} else if (chAfterWord === charCodes.questionMark) {
optional = true;
const startLoc = this.state.startLoc;
const wordName = this.state.value;
const typeOrLabel = this.tsParseNonArrayType();

if (this.lookaheadCharCode() === charCodes.colon) {
labeled = true;
label = this.createIdentifier(
this.startNodeAt<N.Identifier>(startLoc),
wordName,
);
this.expect(tt.question);
this.expect(tt.colon);
type = this.tsParseType();
} else {
labeled = false;
type = typeOrLabel;
this.expect(tt.question);
}
} else {
type = this.tsParseType();
optional = this.eat(tt.question);
// In this case (labeled === true) could be only in invalid label.
// E.g. [x.y:type]
// An error is raised while processing node.
labeled = this.eat(tt.colon);
}

if (
type.type === "TSTypeReference" &&
!type.typeParameters &&
type.typeName.type === "Identifier"
) {
labeledNode.label = type.typeName;
if (labeled) {
let labeledNode: Undone<N.TsNamedTupleMember>;
if (label) {
labeledNode = this.startNodeAtNode<N.TsNamedTupleMember>(label);
labeledNode.optional = optional;
labeledNode.label = label;
labeledNode.elementType = type;

if (this.eat(tt.question)) {
labeledNode.optional = true;
this.raise(TSErrors.TupleOptionalAfterType, {
at: this.state.lastTokStartLoc,
});
}
} else {
labeledNode = this.startNodeAtNode<N.TsNamedTupleMember>(type);
labeledNode.optional = optional;
this.raise(TSErrors.InvalidTupleMemberLabel, { at: type });
// @ts-expect-error This produces an invalid AST, but at least we don't drop
// nodes representing the invalid source.
labeledNode.label = type;
labeledNode.elementType = this.tsParseType();
}

labeledNode.elementType = this.tsParseType();
type = this.finishNode(labeledNode, "TSNamedTupleMember");
} else if (optional) {
const optionalTypeNode = this.startNodeAtNode<N.TsOptionalType>(type);
Expand Down
@@ -0,0 +1,5 @@
type FuncWithDescriptionBabel7 = [
function: (...args: any[]) => any,
string: string,
void?: number
]
@@ -0,0 +1,3 @@
{
"BABEL_8_BREAKING": false
}
@@ -0,0 +1,101 @@
{
"type": "File",
"start":0,"end":107,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":5,"column":1,"index":107}},
"program": {
"type": "Program",
"start":0,"end":107,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":5,"column":1,"index":107}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "TSTypeAliasDeclaration",
"start":0,"end":107,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":5,"column":1,"index":107}},
"id": {
"type": "Identifier",
"start":5,"end":30,"loc":{"start":{"line":1,"column":5,"index":5},"end":{"line":1,"column":30,"index":30},"identifierName":"FuncWithDescriptionBabel7"},
"name": "FuncWithDescriptionBabel7"
},
"typeAnnotation": {
"type": "TSTupleType",
"start":33,"end":107,"loc":{"start":{"line":1,"column":33,"index":33},"end":{"line":5,"column":1,"index":107}},
"elementTypes": [
{
"type": "TSNamedTupleMember",
"start":37,"end":70,"loc":{"start":{"line":2,"column":2,"index":37},"end":{"line":2,"column":35,"index":70}},
"optional": false,
"label": {
"type": "Identifier",
"start":37,"end":45,"loc":{"start":{"line":2,"column":2,"index":37},"end":{"line":2,"column":10,"index":45},"identifierName":"function"},
"name": "function"
},
"elementType": {
"type": "TSFunctionType",
"start":47,"end":70,"loc":{"start":{"line":2,"column":12,"index":47},"end":{"line":2,"column":35,"index":70}},
"parameters": [
{
"type": "RestElement",
"start":48,"end":62,"loc":{"start":{"line":2,"column":13,"index":48},"end":{"line":2,"column":27,"index":62}},
"argument": {
"type": "Identifier",
"start":51,"end":55,"loc":{"start":{"line":2,"column":16,"index":51},"end":{"line":2,"column":20,"index":55},"identifierName":"args"},
"name": "args"
},
"typeAnnotation": {
"type": "TSTypeAnnotation",
"start":55,"end":62,"loc":{"start":{"line":2,"column":20,"index":55},"end":{"line":2,"column":27,"index":62}},
"typeAnnotation": {
"type": "TSArrayType",
"start":57,"end":62,"loc":{"start":{"line":2,"column":22,"index":57},"end":{"line":2,"column":27,"index":62}},
"elementType": {
"type": "TSAnyKeyword",
"start":57,"end":60,"loc":{"start":{"line":2,"column":22,"index":57},"end":{"line":2,"column":25,"index":60}}
}
}
}
}
],
"typeAnnotation": {
"type": "TSTypeAnnotation",
"start":64,"end":70,"loc":{"start":{"line":2,"column":29,"index":64},"end":{"line":2,"column":35,"index":70}},
"typeAnnotation": {
"type": "TSAnyKeyword",
"start":67,"end":70,"loc":{"start":{"line":2,"column":32,"index":67},"end":{"line":2,"column":35,"index":70}}
}
}
}
},
{
"type": "TSNamedTupleMember",
"start":74,"end":88,"loc":{"start":{"line":3,"column":2,"index":74},"end":{"line":3,"column":16,"index":88}},
"optional": false,
"label": {
"type": "Identifier",
"start":74,"end":80,"loc":{"start":{"line":3,"column":2,"index":74},"end":{"line":3,"column":8,"index":80},"identifierName":"string"},
"name": "string"
},
"elementType": {
"type": "TSStringKeyword",
"start":82,"end":88,"loc":{"start":{"line":3,"column":10,"index":82},"end":{"line":3,"column":16,"index":88}}
}
},
{
"type": "TSNamedTupleMember",
"start":92,"end":105,"loc":{"start":{"line":4,"column":2,"index":92},"end":{"line":4,"column":15,"index":105}},
"optional": true,
"label": {
"type": "Identifier",
"start":92,"end":96,"loc":{"start":{"line":4,"column":2,"index":92},"end":{"line":4,"column":6,"index":96},"identifierName":"void"},
"name": "void"
},
"elementType": {
"type": "TSNumberKeyword",
"start":99,"end":105,"loc":{"start":{"line":4,"column":9,"index":99},"end":{"line":4,"column":15,"index":105}}
}
}
]
}
}
],
"directives": []
}
}
@@ -0,0 +1,5 @@
type FuncWithDescription = [
function: (...args: any[]) => any,
string: string,
void?: number
]
@@ -0,0 +1,3 @@
{
"BABEL_8_BREAKING": true
}
@@ -0,0 +1,101 @@
{
"type": "File",
"start":0,"end":101,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":5,"column":1,"index":101}},
"program": {
"type": "Program",
"start":0,"end":101,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":5,"column":1,"index":101}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "TSTypeAliasDeclaration",
"start":0,"end":101,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":5,"column":1,"index":101}},
"id": {
"type": "Identifier",
"start":5,"end":24,"loc":{"start":{"line":1,"column":5,"index":5},"end":{"line":1,"column":24,"index":24},"identifierName":"FuncWithDescription"},
"name": "FuncWithDescription"
},
"typeAnnotation": {
"type": "TSTupleType",
"start":27,"end":101,"loc":{"start":{"line":1,"column":27,"index":27},"end":{"line":5,"column":1,"index":101}},
"elementTypes": [
{
"type": "TSNamedTupleMember",
"start":31,"end":64,"loc":{"start":{"line":2,"column":2,"index":31},"end":{"line":2,"column":35,"index":64}},
"optional": false,
"label": {
"type": "Identifier",
"start":31,"end":39,"loc":{"start":{"line":2,"column":2,"index":31},"end":{"line":2,"column":10,"index":39},"identifierName":"function"},
"name": "function"
},
"elementType": {
"type": "TSFunctionType",
"start":41,"end":64,"loc":{"start":{"line":2,"column":12,"index":41},"end":{"line":2,"column":35,"index":64}},
"params": [
{
"type": "RestElement",
"start":42,"end":56,"loc":{"start":{"line":2,"column":13,"index":42},"end":{"line":2,"column":27,"index":56}},
"argument": {
"type": "Identifier",
"start":45,"end":49,"loc":{"start":{"line":2,"column":16,"index":45},"end":{"line":2,"column":20,"index":49},"identifierName":"args"},
"name": "args"
},
"typeAnnotation": {
"type": "TSTypeAnnotation",
"start":49,"end":56,"loc":{"start":{"line":2,"column":20,"index":49},"end":{"line":2,"column":27,"index":56}},
"typeAnnotation": {
"type": "TSArrayType",
"start":51,"end":56,"loc":{"start":{"line":2,"column":22,"index":51},"end":{"line":2,"column":27,"index":56}},
"elementType": {
"type": "TSAnyKeyword",
"start":51,"end":54,"loc":{"start":{"line":2,"column":22,"index":51},"end":{"line":2,"column":25,"index":54}}
}
}
}
}
],
"returnType": {
"type": "TSTypeAnnotation",
"start":58,"end":64,"loc":{"start":{"line":2,"column":29,"index":58},"end":{"line":2,"column":35,"index":64}},
"typeAnnotation": {
"type": "TSAnyKeyword",
"start":61,"end":64,"loc":{"start":{"line":2,"column":32,"index":61},"end":{"line":2,"column":35,"index":64}}
}
}
}
},
{
"type": "TSNamedTupleMember",
"start":68,"end":82,"loc":{"start":{"line":3,"column":2,"index":68},"end":{"line":3,"column":16,"index":82}},
"optional": false,
"label": {
"type": "Identifier",
"start":68,"end":74,"loc":{"start":{"line":3,"column":2,"index":68},"end":{"line":3,"column":8,"index":74},"identifierName":"string"},
"name": "string"
},
"elementType": {
"type": "TSStringKeyword",
"start":76,"end":82,"loc":{"start":{"line":3,"column":10,"index":76},"end":{"line":3,"column":16,"index":82}}
}
},
{
"type": "TSNamedTupleMember",
"start":86,"end":99,"loc":{"start":{"line":4,"column":2,"index":86},"end":{"line":4,"column":15,"index":99}},
"optional": true,
"label": {
"type": "Identifier",
"start":86,"end":90,"loc":{"start":{"line":4,"column":2,"index":86},"end":{"line":4,"column":6,"index":90},"identifierName":"void"},
"name": "void"
},
"elementType": {
"type": "TSNumberKeyword",
"start":93,"end":99,"loc":{"start":{"line":4,"column":9,"index":93},"end":{"line":4,"column":15,"index":99}}
}
}
]
}
}
],
"directives": []
}
}
Expand Up @@ -2,6 +2,5 @@
"sourceType": "module",
"plugins": [
"typescript"
],
"throws": "Unexpected token, expected \",\" (1:14)"
}
]
}
@@ -0,0 +1,50 @@
{
"type": "File",
"start":0,"end":17,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":17,"index":17}},
"errors": [
"SyntaxError: A labeled tuple optional element must be declared using a question mark after the name and before the colon (`name?: type`), rather than after the type (`name: type?`). (1:14)"
],
"program": {
"type": "Program",
"start":0,"end":17,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":17,"index":17}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "TSTypeAliasDeclaration",
"start":0,"end":17,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":17,"index":17}},
"id": {
"type": "Identifier",
"start":5,"end":6,"loc":{"start":{"line":1,"column":5,"index":5},"end":{"line":1,"column":6,"index":6},"identifierName":"T"},
"name": "T"
},
"typeAnnotation": {
"type": "TSTupleType",
"start":9,"end":16,"loc":{"start":{"line":1,"column":9,"index":9},"end":{"line":1,"column":16,"index":16}},
"elementTypes": [
{
"type": "TSNamedTupleMember",
"start":10,"end":15,"loc":{"start":{"line":1,"column":10,"index":10},"end":{"line":1,"column":15,"index":15}},
"optional": true,
"label": {
"type": "Identifier",
"start":10,"end":11,"loc":{"start":{"line":1,"column":10,"index":10},"end":{"line":1,"column":11,"index":11},"identifierName":"x"},
"name": "x"
},
"elementType": {
"type": "TSTypeReference",
"start":13,"end":14,"loc":{"start":{"line":1,"column":13,"index":13},"end":{"line":1,"column":14,"index":14}},
"typeName": {
"type": "Identifier",
"start":13,"end":14,"loc":{"start":{"line":1,"column":13,"index":13},"end":{"line":1,"column":14,"index":14},"identifierName":"A"},
"name": "A"
}
}
}
]
}
}
],
"directives": []
}
}

0 comments on commit 80863c2

Please sign in to comment.