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",

View File

@@ -0,0 +1,3 @@
class A {
declare bar: string = "test";
}

View File

@@ -0,0 +1,170 @@
{
"type": "File",
"start": 0,
"end": 43,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 3,
"column": 1
}
},
"program": {
"type": "Program",
"start": 0,
"end": 43,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 3,
"column": 1
}
},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start": 0,
"end": 43,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 3,
"column": 1
}
},
"id": {
"type": "Identifier",
"start": 6,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 6
},
"end": {
"line": 1,
"column": 7
},
"identifierName": "A"
},
"name": "A"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start": 8,
"end": 43,
"loc": {
"start": {
"line": 1,
"column": 8
},
"end": {
"line": 3,
"column": 1
}
},
"body": [
{
"type": "ClassProperty",
"start": 12,
"end": 41,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 31
}
},
"declare": true,
"static": false,
"key": {
"type": "Identifier",
"start": 20,
"end": 23,
"loc": {
"start": {
"line": 2,
"column": 10
},
"end": {
"line": 2,
"column": 13
},
"identifierName": "bar"
},
"name": "bar"
},
"computed": false,
"typeAnnotation": {
"type": "TSTypeAnnotation",
"start": 23,
"end": 31,
"loc": {
"start": {
"line": 2,
"column": 13
},
"end": {
"line": 2,
"column": 21
}
},
"typeAnnotation": {
"type": "TSStringKeyword",
"start": 25,
"end": 31,
"loc": {
"start": {
"line": 2,
"column": 15
},
"end": {
"line": 2,
"column": 21
}
}
}
},
"value": {
"type": "StringLiteral",
"start": 34,
"end": 40,
"loc": {
"start": {
"line": 2,
"column": 24
},
"end": {
"line": 2,
"column": 30
}
},
"extra": {
"rawValue": "test",
"raw": "\"test\""
},
"value": "test"
}
}
]
}
}
],
"directives": []
}
}

View File

@@ -0,0 +1,4 @@
class A {
declare foo;
declare bar: string;
}

View File

@@ -0,0 +1,187 @@
{
"type": "File",
"start": 0,
"end": 49,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"program": {
"type": "Program",
"start": 0,
"end": 49,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start": 0,
"end": 49,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"id": {
"type": "Identifier",
"start": 6,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 6
},
"end": {
"line": 1,
"column": 7
},
"identifierName": "A"
},
"name": "A"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start": 8,
"end": 49,
"loc": {
"start": {
"line": 1,
"column": 8
},
"end": {
"line": 4,
"column": 1
}
},
"body": [
{
"type": "ClassProperty",
"start": 12,
"end": 24,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 14
}
},
"declare": true,
"static": false,
"key": {
"type": "Identifier",
"start": 20,
"end": 23,
"loc": {
"start": {
"line": 2,
"column": 10
},
"end": {
"line": 2,
"column": 13
},
"identifierName": "foo"
},
"name": "foo"
},
"computed": false,
"value": null
},
{
"type": "ClassProperty",
"start": 27,
"end": 47,
"loc": {
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 22
}
},
"declare": true,
"static": false,
"key": {
"type": "Identifier",
"start": 35,
"end": 38,
"loc": {
"start": {
"line": 3,
"column": 10
},
"end": {
"line": 3,
"column": 13
},
"identifierName": "bar"
},
"name": "bar"
},
"computed": false,
"typeAnnotation": {
"type": "TSTypeAnnotation",
"start": 38,
"end": 46,
"loc": {
"start": {
"line": 3,
"column": 13
},
"end": {
"line": 3,
"column": 21
}
},
"typeAnnotation": {
"type": "TSStringKeyword",
"start": 40,
"end": 46,
"loc": {
"start": {
"line": 3,
"column": 15
},
"end": {
"line": 3,
"column": 21
}
}
}
},
"value": null
}
]
}
}
],
"directives": []
}
}

View File

@@ -0,0 +1,3 @@
class A {
declare foo() {}
}

View File

@@ -0,0 +1,145 @@
{
"type": "File",
"start": 0,
"end": 30,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 3,
"column": 1
}
},
"errors": [
"SyntaxError: Class methods cannot have the 'declare' modifier (2:2)"
],
"program": {
"type": "Program",
"start": 0,
"end": 30,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 3,
"column": 1
}
},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start": 0,
"end": 30,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 3,
"column": 1
}
},
"id": {
"type": "Identifier",
"start": 6,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 6
},
"end": {
"line": 1,
"column": 7
},
"identifierName": "A"
},
"name": "A"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start": 8,
"end": 30,
"loc": {
"start": {
"line": 1,
"column": 8
},
"end": {
"line": 3,
"column": 1
}
},
"body": [
{
"type": "ClassMethod",
"start": 12,
"end": 28,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 18
}
},
"declare": true,
"static": false,
"key": {
"type": "Identifier",
"start": 20,
"end": 23,
"loc": {
"start": {
"line": 2,
"column": 10
},
"end": {
"line": 2,
"column": 13
},
"identifierName": "foo"
},
"name": "foo"
},
"computed": false,
"kind": "method",
"id": null,
"generator": false,
"async": false,
"params": [],
"body": {
"type": "BlockStatement",
"start": 26,
"end": 28,
"loc": {
"start": {
"line": 2,
"column": 16
},
"end": {
"line": 2,
"column": 18
}
},
"body": [],
"directives": []
}
}
]
}
}
],
"directives": []
}
}

View File

@@ -1,3 +0,0 @@
{
"throws": "Unexpected token, expected \";\" (2:14)"
}

View File

@@ -0,0 +1,145 @@
{
"type": "File",
"start": 0,
"end": 31,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 3,
"column": 1
}
},
"errors": [
"SyntaxError: Class methods cannot have the 'readonly' modifier (2:4)"
],
"program": {
"type": "Program",
"start": 0,
"end": 31,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 3,
"column": 1
}
},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start": 0,
"end": 31,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 3,
"column": 1
}
},
"id": {
"type": "Identifier",
"start": 6,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 6
},
"end": {
"line": 1,
"column": 7
},
"identifierName": "C"
},
"name": "C"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start": 8,
"end": 31,
"loc": {
"start": {
"line": 1,
"column": 8
},
"end": {
"line": 3,
"column": 1
}
},
"body": [
{
"type": "ClassMethod",
"start": 14,
"end": 29,
"loc": {
"start": {
"line": 2,
"column": 4
},
"end": {
"line": 2,
"column": 19
}
},
"readonly": true,
"static": false,
"key": {
"type": "Identifier",
"start": 23,
"end": 24,
"loc": {
"start": {
"line": 2,
"column": 13
},
"end": {
"line": 2,
"column": 14
},
"identifierName": "m"
},
"name": "m"
},
"computed": false,
"kind": "method",
"id": null,
"generator": false,
"async": false,
"params": [],
"body": {
"type": "BlockStatement",
"start": 27,
"end": 29,
"loc": {
"start": {
"line": 2,
"column": 17
},
"end": {
"line": 2,
"column": 19
}
},
"body": [],
"directives": []
}
}
]
}
}
],
"directives": []
}
}

View File

@@ -77,7 +77,7 @@
},
"body": [
{
"type": "ClassProperty",
"type": "ClassPrivateProperty",
"start": 12,
"end": 24,
"loc": {
@@ -127,7 +127,7 @@
"value": null
},
{
"type": "ClassProperty",
"type": "ClassPrivateProperty",
"start": 27,
"end": 47,
"loc": {