Implement support for declare on class fields with Flow (#11178)
* Add parser support for Flow declare fields * Add generator test * Add "allowDeclareFields" option to flow-strip-types * Add test * Update error messages * More tests
This commit is contained in:
@@ -1240,50 +1240,59 @@ export default class StatementParser extends ExpressionParser {
|
||||
return this.finishNode(classBody, "ClassBody");
|
||||
}
|
||||
|
||||
// returns true if the current identifier is a method/field name,
|
||||
// false if it is a modifier
|
||||
parseClassMemberFromModifier(
|
||||
classBody: N.ClassBody,
|
||||
member: N.ClassMember,
|
||||
): boolean {
|
||||
const containsEsc = this.state.containsEsc;
|
||||
const key = this.parseIdentifier(true); // eats the modifier
|
||||
|
||||
if (this.isClassMethod()) {
|
||||
const method: N.ClassMethod = (member: any);
|
||||
|
||||
// a method named like the modifier
|
||||
method.kind = "method";
|
||||
method.computed = false;
|
||||
method.key = key;
|
||||
method.static = false;
|
||||
this.pushClassMethod(
|
||||
classBody,
|
||||
method,
|
||||
false,
|
||||
false,
|
||||
/* isConstructor */ false,
|
||||
false,
|
||||
);
|
||||
return true;
|
||||
} else if (this.isClassProperty()) {
|
||||
const prop: N.ClassProperty = (member: any);
|
||||
|
||||
// a property named like the modifier
|
||||
prop.computed = false;
|
||||
prop.key = key;
|
||||
prop.static = false;
|
||||
classBody.body.push(this.parseClassProperty(prop));
|
||||
return true;
|
||||
} else if (containsEsc) {
|
||||
throw this.unexpected();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
parseClassMember(
|
||||
classBody: N.ClassBody,
|
||||
member: N.ClassMember,
|
||||
state: { hadConstructor: boolean },
|
||||
constructorAllowsSuper: boolean,
|
||||
): void {
|
||||
let isStatic = false;
|
||||
const containsEsc = this.state.containsEsc;
|
||||
const isStatic = this.isContextual("static");
|
||||
|
||||
if (this.match(tt.name) && this.state.value === "static") {
|
||||
const key = this.parseIdentifier(true); // eats 'static'
|
||||
|
||||
if (this.isClassMethod()) {
|
||||
const method: N.ClassMethod = (member: any);
|
||||
|
||||
// a method named 'static'
|
||||
method.kind = "method";
|
||||
method.computed = false;
|
||||
method.key = key;
|
||||
method.static = false;
|
||||
this.pushClassMethod(
|
||||
classBody,
|
||||
method,
|
||||
false,
|
||||
false,
|
||||
/* isConstructor */ false,
|
||||
false,
|
||||
);
|
||||
return;
|
||||
} else if (this.isClassProperty()) {
|
||||
const prop: N.ClassProperty = (member: any);
|
||||
|
||||
// a property named 'static'
|
||||
prop.computed = false;
|
||||
prop.key = key;
|
||||
prop.static = false;
|
||||
classBody.body.push(this.parseClassProperty(prop));
|
||||
return;
|
||||
} else if (containsEsc) {
|
||||
throw this.unexpected();
|
||||
}
|
||||
|
||||
// otherwise something static
|
||||
isStatic = true;
|
||||
if (isStatic && this.parseClassMemberFromModifier(classBody, member)) {
|
||||
// a class element named 'static'
|
||||
return;
|
||||
}
|
||||
|
||||
this.parseClassMemberWithIsStatic(
|
||||
|
||||
@@ -51,6 +51,10 @@ const FlowErrors = Object.freeze({
|
||||
AmbiguousDeclareModuleKind:
|
||||
"Found both `declare module.exports` and `declare export` in the same module. Modules can only have 1 since they are either an ES module or they are a CommonJS module",
|
||||
AssignReservedType: "Cannot overwrite reserved type %0",
|
||||
DeclareClassElement:
|
||||
"The `declare` modifier can only appear on class fields.",
|
||||
DeclareClassFieldInitializer:
|
||||
"Initializers are not allowed in fields with the `declare` modifier.",
|
||||
DuplicateDeclareModuleExports: "Duplicate `declare module.exports` statement",
|
||||
EnumBooleanMemberNotInitialized:
|
||||
"Boolean enum members need to be initialized. Use either `%0 = true,` or `%0 = false,` in enum `%1`.",
|
||||
@@ -2085,6 +2089,39 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
}
|
||||
}
|
||||
|
||||
parseClassMember(
|
||||
classBody: N.ClassBody,
|
||||
member: any,
|
||||
state: { hadConstructor: boolean },
|
||||
constructorAllowsSuper: boolean,
|
||||
): void {
|
||||
const pos = this.state.start;
|
||||
if (this.isContextual("declare")) {
|
||||
if (this.parseClassMemberFromModifier(classBody, member)) {
|
||||
// 'declare' is a class element name
|
||||
return;
|
||||
}
|
||||
|
||||
member.declare = true;
|
||||
}
|
||||
|
||||
super.parseClassMember(classBody, member, state, constructorAllowsSuper);
|
||||
|
||||
if (member.declare) {
|
||||
if (
|
||||
member.type !== "ClassProperty" &&
|
||||
member.type !== "ClassPrivateProperty"
|
||||
) {
|
||||
this.raise(pos, FlowErrors.DeclareClassElement);
|
||||
} else if (member.value) {
|
||||
this.raise(
|
||||
member.value.start,
|
||||
FlowErrors.DeclareClassFieldInitializer,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that inside flow types, we bypass the jsx parser plugin
|
||||
getTokenFromCode(code: number): void {
|
||||
const next = this.input.charCodeAt(this.state.pos + 1);
|
||||
|
||||
Reference in New Issue
Block a user