Add support for TS declare modifier on fields (#10545)

* [parser] Add support for TS declare modifier on fields (#10484)

* [parser] Add support for TS declare modifier on fields

* Use Object.create(null)

* Comment

* Add support for TS declare types to types and generator (#10544)

* Transform TypeScript "declare" fields (#10546)

* Transform TypeScript "declare" fields

* Remove multiple spaces

* declareFields -> allowDeclareFields

* Update after rebase
This commit is contained in:
Nicolò Ribaudo
2019-11-05 10:56:57 +01:00
committed by GitHub
parent 87feda7c2a
commit e9c1bce50f
38 changed files with 984 additions and 165 deletions

View File

@@ -28,6 +28,7 @@ import * as charCodes from "charcodes";
type TsModifier =
| "readonly"
| "abstract"
| "declare"
| "static"
| "public"
| "private"
@@ -129,6 +130,31 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return undefined;
}
/** Parses a list of modifiers, in any order.
* If you need a specific order, you must call this function multiple times:
* this.tsParseModifiers(["public"]);
* this.tsParseModifiers(["abstract", "readonly"]);
*/
tsParseModifiers<T: TsModifier>(
allowedModifiers: T[],
): { [key: TsModifier]: ?true, __proto__: null } {
const modifiers = Object.create(null);
while (true) {
const startPos = this.state.start;
const modifier: ?T = this.tsParseModifier(allowedModifiers);
if (!modifier) break;
if (Object.hasOwnProperty.call(modifiers, modifier)) {
this.raise(startPos, `Duplicate modifier: '${modifier}'`);
}
modifiers[modifier] = true;
}
return modifiers;
}
tsIsListTerminator(kind: ParsingContext): boolean {
switch (kind) {
case "EnumMembers":
@@ -405,7 +431,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return this.eat(tt.name) && this.match(tt.colon);
}
tsTryParseIndexSignature(node: N.TsIndexSignature): ?N.TsIndexSignature {
tsTryParseIndexSignature(node: N.Node): ?N.TsIndexSignature {
if (
!(
this.match(tt.bracketL) &&
@@ -1844,50 +1870,49 @@ export default (superClass: Class<Parser>): Class<Parser> =>
parseClassMemberWithIsStatic(
classBody: N.ClassBody,
member: any,
member: N.ClassMember | N.TsIndexSignature,
state: { hadConstructor: boolean },
isStatic: boolean,
constructorAllowsSuper: boolean,
): void {
const methodOrProp: N.ClassMethod | N.ClassProperty = member;
const prop: N.ClassProperty = member;
const propOrIdx: N.ClassProperty | N.TsIndexSignature = member;
const modifiers = this.tsParseModifiers([
"abstract",
"readonly",
"declare",
]);
let abstract = false,
readonly = false;
Object.assign(member, modifiers);
const mod = this.tsParseModifier(["abstract", "readonly"]);
switch (mod) {
case "readonly":
readonly = true;
abstract = !!this.tsParseModifier(["abstract"]);
break;
case "abstract":
abstract = true;
readonly = !!this.tsParseModifier(["readonly"]);
break;
}
const idx = this.tsTryParseIndexSignature(member);
if (idx) {
classBody.body.push(idx);
if (abstract) methodOrProp.abstract = true;
if (readonly) propOrIdx.readonly = true;
if (!abstract && !isStatic && !methodOrProp.accessibility) {
const idx = this.tsTryParseIndexSignature(member);
if (idx) {
classBody.body.push(idx);
return;
if (modifiers.abstract) {
this.raise(
member.start,
"Index signatures cannot have the 'abstract' modifier",
);
}
if (isStatic) {
this.raise(
member.start,
"Index signatures cannot have the 'static' modifier",
);
}
if ((member: any).accessibility) {
this.raise(
member.start,
`Index signatures cannot have an accessibility modifier ('${
(member: any).accessibility
}')`,
);
}
}
if (readonly) {
// Must be a property (if not an index signature).
methodOrProp.static = isStatic;
this.parseClassPropertyName(prop);
this.parsePostMemberNameModifiers(methodOrProp);
this.pushClassProperty(classBody, prop);
return;
}
/*:: invariant(member.type !== "TSIndexSignature") */
super.parseClassMemberWithIsStatic(
classBody,
member,
@@ -1902,6 +1927,20 @@ export default (superClass: Class<Parser>): Class<Parser> =>
): void {
const optional = this.eat(tt.question);
if (optional) methodOrProp.optional = true;
if ((methodOrProp: any).readonly && this.match(tt.parenL)) {
this.raise(
methodOrProp.start,
"Class methods cannot have the 'readonly' modifier",
);
}
if ((methodOrProp: any).declare && this.match(tt.parenL)) {
this.raise(
methodOrProp.start,
"Class methods cannot have the 'declare' modifier",
);
}
}
// Note: The reason we do this in `parseExpressionStatement` and not `parseStatement`
@@ -2048,6 +2087,14 @@ export default (superClass: Class<Parser>): Class<Parser> =>
parseClassProperty(node: N.ClassProperty): N.ClassProperty {
this.parseClassPropertyAnnotation(node);
if (node.declare && this.match(tt.equal)) {
this.raise(
this.state.start,
"'declare' class fields cannot have an initializer",
);
}
return super.parseClassProperty(node);
}

View File

@@ -743,18 +743,19 @@ export type ClassPrivateMethod = NodeBase &
computed: false,
};
export type ClassProperty = ClassMemberBase & {
type: "ClassProperty",
key: Expression,
value: ?Expression, // TODO: Not in spec that this is nullable.
export type ClassProperty = ClassMemberBase &
DeclarationBase & {
type: "ClassProperty",
key: Expression,
value: ?Expression, // TODO: Not in spec that this is nullable.
typeAnnotation?: ?TypeAnnotationBase, // TODO: Not in spec
variance?: ?FlowVariance, // TODO: Not in spec
typeAnnotation?: ?TypeAnnotationBase, // TODO: Not in spec
variance?: ?FlowVariance, // TODO: Not in spec
// TypeScript only: (TODO: Not in spec)
readonly?: true,
definite?: true,
};
// TypeScript only: (TODO: Not in spec)
readonly?: true,
definite?: true,
};
export type ClassPrivateProperty = NodeBase & {
type: "ClassPrivateProperty",