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

TypeScript 4.0 support #11760

Merged
merged 4 commits into from Jul 29, 2020
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
2 changes: 1 addition & 1 deletion Makefile
@@ -1,6 +1,6 @@
FLOW_COMMIT = a1f9a4c709dcebb27a5084acf47755fbae699c25
TEST262_COMMIT = 058adfed86b1d4129996faaf50a85ea55379a66a
TYPESCRIPT_COMMIT = 5fc917be2e4dd64c8e9504d36615cd7fbfdd4cd3
TYPESCRIPT_COMMIT = ffa35d3272647fe48ddf173e1f0928f772c18630

FORCE_PUBLISH = "@babel/runtime,@babel/runtime-corejs2,@babel/runtime-corejs3,@babel/standalone"

Expand Down
1 change: 1 addition & 0 deletions packages/babel-generator/src/generators/statements.js
Expand Up @@ -174,6 +174,7 @@ export function CatchClause(node: Object) {
if (node.param) {
this.token("(");
this.print(node.param, node);
this.print(node.param.typeAnnotation, node);
this.token(")");
this.space();
}
Expand Down
8 changes: 8 additions & 0 deletions packages/babel-generator/src/generators/typescript.js
Expand Up @@ -262,6 +262,14 @@ export function TSRestType(node) {
this.print(node.typeAnnotation, node);
}

export function TSNamedTupleMember(node) {
this.print(node.label, node);
if (node.optional) this.token("?");
this.token(":");
this.space();
this.print(node.elementType, node);
}

export function TSUnionType(node) {
this.tsPrintUnionOrIntersectionType(node, "|");
}
Expand Down
@@ -0,0 +1,2 @@
try {} catch (e: unknown) {}
try {} catch (e: any) {}
@@ -0,0 +1,3 @@
try {} catch (e: unknown) {}

try {} catch (e: any) {}
@@ -0,0 +1 @@
type T = [x: A, y?: B, ...z: C];
@@ -0,0 +1 @@
type T = [x: A, y?: B, ...z: C];
15 changes: 11 additions & 4 deletions packages/babel-parser/src/parser/statement.js
Expand Up @@ -636,6 +636,16 @@ export default class StatementParser extends ExpressionParser {
return this.finishNode(node, "ThrowStatement");
}

parseCatchClauseParam(): N.Identifier {
const param = this.parseBindingAtom();

const simple = param.type === "Identifier";
this.scope.enter(simple ? SCOPE_SIMPLE_CATCH : 0);
this.checkLVal(param, BIND_LEXICAL, null, "catch clause");

return param;
}

parseTryStatement(node: N.TryStatement): N.TryStatement {
this.next();

Expand All @@ -647,10 +657,7 @@ export default class StatementParser extends ExpressionParser {
this.next();
if (this.match(tt.parenL)) {
this.expect(tt.parenL);
clause.param = this.parseBindingAtom();
const simple = clause.param.type === "Identifier";
this.scope.enter(simple ? SCOPE_SIMPLE_CATCH : 0);
this.checkLVal(clause.param, BIND_LEXICAL, null, "catch clause");
clause.param = this.parseCatchClauseParam();
this.expect(tt.parenR);
} else {
clause.param = null;
Expand Down
102 changes: 82 additions & 20 deletions packages/babel-parser/src/plugins/typescript/index.js
Expand Up @@ -74,6 +74,10 @@ const TSErrors = Object.freeze({
IndexSignatureHasAccessibility:
"Index signatures cannot have an accessibility modifier ('%0')",
IndexSignatureHasStatic: "Index signatures cannot have the 'static' modifier",
InvalidTupleMemberLabel:
"Tuple members must be labeled with a simple identifier.",
MixedLabeledAndUnlabeledElements:
"Tuple members must all have names or all not have names.",
OptionalTypeBeforeRequired:
"A required element cannot follow an optional element.",
PatternIsOptional:
Expand Down Expand Up @@ -630,43 +634,90 @@ export default (superClass: Class<Parser>): Class<Parser> =>
/* skipFirstToken */ false,
);

// Validate the elementTypes to ensure:
// No mandatory elements may follow optional elements
// If there's a rest element, it must be at the end of the tuple
// Validate the elementTypes to ensure that no mandatory elements
// follow optional elements
let seenOptionalElement = false;
let labeledElements = null;
node.elementTypes.forEach(elementNode => {
if (elementNode.type === "TSOptionalType") {
seenOptionalElement = true;
} else if (seenOptionalElement && elementNode.type !== "TSRestType") {
let { type } = elementNode;

if (
seenOptionalElement &&
type !== "TSRestType" &&
type !== "TSOptionalType" &&
!(type === "TSNamedTupleMember" && elementNode.optional)
) {
this.raise(elementNode.start, TSErrors.OptionalTypeBeforeRequired);
}

// Flow doesn't support ||=
seenOptionalElement =
seenOptionalElement ||
(type === "TSNamedTupleMember" && elementNode.optional) ||
type === "TSOptionalType";

// When checking labels, check the argument of the spread operator
if (type === "TSRestType") {
elementNode = elementNode.typeAnnotation;
type = elementNode.type;
}

const isLabeled = type === "TSNamedTupleMember";
// Flow doesn't support ??=
labeledElements = labeledElements ?? isLabeled;
if (labeledElements !== isLabeled) {
this.raise(
elementNode.start,
TSErrors.MixedLabeledAndUnlabeledElements,
);
}
});

return this.finishNode(node, "TSTupleType");
}

tsParseTupleElementType(): N.TsType {
tsParseTupleElementType(): N.TsType | N.TsNamedTupleMember {
// parses `...TsType[]`
if (this.match(tt.ellipsis)) {
const restNode: N.TsRestType = this.startNode();
this.next(); // skips ellipsis
restNode.typeAnnotation = this.tsParseType();

const { start: startPos, startLoc } = this.state;

const rest = this.eat(tt.ellipsis);
let type = this.tsParseType();
const optional = this.eat(tt.question);
const labeled = this.eat(tt.colon);

if (labeled) {
const labeledNode: N.TsNamedTupleMember = this.startNodeAtNode(type);
labeledNode.optional = optional;

if (
this.match(tt.comma) &&
this.lookaheadCharCode() !== charCodes.rightSquareBracket
type.type === "TSTypeReference" &&
!type.typeParameters &&
type.typeName.type === "Identifier"
) {
this.raiseRestNotLast(this.state.start);
labeledNode.label = (type.typeName: N.Identifier);
} else {
this.raise(type.start, TSErrors.InvalidTupleMemberLabel);
// This produces an invalid AST, but at least we don't drop
// nodes representing the invalid source.
// $FlowIgnore
labeledNode.label = type;
}
return this.finishNode(restNode, "TSRestType");
}

const type = this.tsParseType();
// parses `TsType?`
if (this.eat(tt.question)) {
labeledNode.elementType = this.tsParseType();
type = this.finishNode(labeledNode, "TSNamedTupleMember");
} else if (optional) {
const optionalTypeNode: N.TsOptionalType = this.startNodeAtNode(type);
optionalTypeNode.typeAnnotation = type;
return this.finishNode(optionalTypeNode, "TSOptionalType");
type = this.finishNode(optionalTypeNode, "TSOptionalType");
}

if (rest) {
const restNode: N.TsRestType = this.startNodeAt(startPos, startLoc);
restNode.typeAnnotation = type;
type = this.finishNode(restNode, "TSRestType");
}

return type;
}

Expand Down Expand Up @@ -2667,4 +2718,15 @@ export default (superClass: Class<Parser>): Class<Parser> =>

return hasContextParam ? baseCount + 1 : baseCount;
}

parseCatchClauseParam(): N.Identifier {
const param = super.parseCatchClauseParam();
const type = this.tsTryParseTypeAnnotation();

if (type) {
param.typeAnnotation = type;
}

return param;
}
};
11 changes: 9 additions & 2 deletions packages/babel-parser/src/types.js
Expand Up @@ -1264,7 +1264,14 @@ export type TsArrayType = TsTypeBase & {

export type TsTupleType = TsTypeBase & {
type: "TSTupleType",
elementTypes: $ReadOnlyArray<TsType>,
elementTypes: $ReadOnlyArray<TsType | TsNamedTupleMember>,
};

export type TsNamedTupleMember = NodeBase & {
type: "TSNamedTupleMember",
label: Identifier,
optional: boolean,
elementType: TsType,
};

export type TsOptionalType = TsTypeBase & {
Expand All @@ -1274,7 +1281,7 @@ export type TsOptionalType = TsTypeBase & {

export type TsRestType = TsTypeBase & {
type: "TSRestType",
typeAnnotation: TsType,
typeAnnotation: TsType | TsNamedTupleMember,
};

export type TsUnionOrIntersectionType = TsUnionType | TsIntersectionType;
Expand Down
@@ -0,0 +1,9 @@
try {} catch (ex) {}
try {} catch (ex: unknown) {}
try {} catch (ex: any) {}

// The following can't be error'd at parse time
try {} catch (ex: A) {}
try {} catch (ex: Error) {}
try {} catch (ex: string) {}
try {} catch (ex: string | number) {}