Support TS 4.3 get/set type members (#13089)

Co-authored-by: Nicolò Ribaudo <nicolo.ribaudo@gmail.com>
This commit is contained in:
Sosuke Suzuki
2021-04-13 06:10:21 +09:00
committed by Nicolò Ribaudo
parent 2521c666f7
commit 7484b51e56
41 changed files with 990 additions and 41 deletions

View File

@@ -2387,11 +2387,6 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return !this.match(tt.colon) && super.isNonstaticConstructor(method);
}
// determine whether a parameter is a this param
isThisParam(param) {
return param.type === "Identifier" && param.name === "this";
}
// parse type parameters for class methods
pushClassMethod(
classBody: N.ClassBody,

View File

@@ -70,12 +70,15 @@ const TSErrors = makeErrorTemplates(
{
AbstractMethodHasImplementation:
"Method '%0' cannot have an implementation because it is marked abstract.",
AccesorCannotDeclareThisParameter:
"'get' and 'set' accessors cannot declare 'this' parameters.",
AccesorCannotHaveTypeParameters: "An accessor cannot have type parameters.",
ClassMethodHasDeclare: "Class methods cannot have the 'declare' modifier.",
ClassMethodHasReadonly:
"Class methods cannot have the 'readonly' modifier.",
ConstructorHasTypeParameters:
"Type parameters cannot appear on a constructor declaration.",
DeclareAccessor: "'declare' is not allowed in %0ters.",
DeclareAccessor: "'declare' is not allowed in %0ters.",
DeclareClassFieldHasInitializer:
"Initializers are not allowed in ambient contexts.",
DeclareFunctionHasImplementation:
@@ -122,6 +125,12 @@ const TSErrors = makeErrorTemplates(
"Private elements cannot have an accessibility modifier ('%0').",
ReadonlyForMethodSignature:
"'readonly' modifier can only appear on a property declaration or index signature.",
SetAccesorCannotHaveOptionalParameter:
"A 'set' accessor cannot have an optional parameter.",
SetAccesorCannotHaveRestParameter:
"A 'set' accessor cannot have rest parameter.",
SetAccesorCannotHaveReturnType:
"A 'set' accessor cannot have a return type annotation.",
TypeAnnotationAfterAssign:
"Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`.",
TypeImportCannotSpecifyDefaultAndNamed:
@@ -193,12 +202,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return this.match(tt.name);
}
tsNextTokenCanFollowModifier() {
// Note: TypeScript's implementation is much more complicated because
// more things are considered modifiers there.
// This implementation only handles modifiers not handled by @babel/parser itself. And "static".
// TODO: Would be nice to avoid lookahead. Want a hasLineBreakUpNext() method...
this.next();
tsTokenCanFollowModifier() {
return (
(this.match(tt.bracketL) ||
this.match(tt.braceL) ||
@@ -210,6 +214,15 @@ export default (superClass: Class<Parser>): Class<Parser> =>
);
}
tsNextTokenCanFollowModifier() {
// Note: TypeScript's implementation is much more complicated because
// more things are considered modifiers there.
// This implementation only handles modifiers not handled by @babel/parser itself. And "static".
// TODO: Would be nice to avoid lookahead. Want a hasLineBreakUpNext() method...
this.next();
return this.tsTokenCanFollowModifier();
}
/** Parses a modifier matching one the given modifier names. */
tsParseModifier<T: TsModifier>(allowedModifiers: T[]): ?T {
if (!this.match(tt.name)) {
@@ -547,8 +560,8 @@ export default (superClass: Class<Parser>): Class<Parser> =>
}
tsParseTypeMemberSemicolon(): void {
if (!this.eat(tt.comma)) {
this.semicolon();
if (!this.eat(tt.comma) && !this.isLineTerminator()) {
this.expect(tt.semi);
}
}
@@ -602,8 +615,57 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.raise(node.start, TSErrors.ReadonlyForMethodSignature);
}
const method: N.TsMethodSignature = nodeAny;
if (method.kind && this.isRelational("<")) {
this.raise(this.state.pos, TSErrors.AccesorCannotHaveTypeParameters);
}
this.tsFillSignature(tt.colon, method);
this.tsParseTypeMemberSemicolon();
if (method.kind === "get") {
if (method.parameters.length > 0) {
this.raise(this.state.pos, Errors.BadGetterArity);
if (this.isThisParam(method.parameters[0])) {
this.raise(
this.state.pos,
TSErrors.AccesorCannotDeclareThisParameter,
);
}
}
} else if (method.kind === "set") {
if (method.parameters.length !== 1) {
this.raise(this.state.pos, Errors.BadSetterArity);
} else {
const firstParameter = method.parameters[0];
if (this.isThisParam(firstParameter)) {
this.raise(
this.state.pos,
TSErrors.AccesorCannotDeclareThisParameter,
);
}
if (
firstParameter.type === "Identifier" &&
firstParameter.optional
) {
this.raise(
this.state.pos,
TSErrors.SetAccesorCannotHaveOptionalParameter,
);
}
if (firstParameter.type === "RestElement") {
this.raise(
this.state.pos,
TSErrors.SetAccesorCannotHaveRestParameter,
);
}
}
if (method.typeAnnotation) {
this.raise(
method.typeAnnotation.start,
TSErrors.SetAccesorCannotHaveReturnType,
);
}
} else {
method.kind = "method";
}
return this.finishNode(method, "TSMethodSignature");
} else {
const property: N.TsPropertySignature = nodeAny;
@@ -657,6 +719,15 @@ export default (superClass: Class<Parser>): Class<Parser> =>
}
this.parsePropertyName(node, /* isPrivateNameAllowed */ false);
if (
!node.computed &&
node.key.type === "Identifier" &&
(node.key.name === "get" || node.key.name === "set") &&
this.tsTokenCanFollowModifier()
) {
node.kind = node.key.name;
this.parsePropertyName(node, /* isPrivateNameAllowed */ false);
}
return this.tsParsePropertyOrMethodSignature(node, !!node.readonly);
}