Skip to content

Commit

Permalink
Support TS 4.3 override syntax in class (#13097)
Browse files Browse the repository at this point in the history
* support TS 4.3 `override` syntax in class

* fix types

* fix types

* tweak error message

* update TypeScript commit

* split tests

* add more tests

* update allowlist

* disallow `override` with `declare`

* disallow `override` in non-sub class

* update TypeScript allowlist

* rename error message key

* add more tests
  • Loading branch information
g-plane committed Apr 7, 2021
1 parent 7b3d94b commit 9571427
Show file tree
Hide file tree
Showing 15 changed files with 693 additions and 9 deletions.
2 changes: 1 addition & 1 deletion Makefile
@@ -1,6 +1,6 @@
FLOW_COMMIT = a1f9a4c709dcebb27a5084acf47755fbae699c25
TEST262_COMMIT = 31126581e7290f9233c29cefd93f66c6ac78f1c9
TYPESCRIPT_COMMIT = 41dc625b0a609eb294b975dd92675e72b2b3fdec
TYPESCRIPT_COMMIT = dd1ef88d016dc40a145eafc0a1169e7f0a4a9ebe

# Fix color output until TravisCI fixes https://github.com/travis-ci/travis-ci/issues/7967
export FORCE_COLOR = true
Expand Down
4 changes: 4 additions & 0 deletions packages/babel-generator/src/generators/typescript.ts
Expand Up @@ -644,6 +644,10 @@ export function tsPrintClassMemberModifiers(this: Printer, node: any, isField) {
this.word("declare");
this.space();
}
if (node.override) {
this.word("override");
this.space();
}
if (node.accessibility) {
this.word(node.accessibility);
this.space();
Expand Down
@@ -0,0 +1,8 @@
class MyClass extends BaseClass {
override show() {}
override public show() {}
public override show() {}
override size = 5;
override readonly size = 5;
readonly override size = 5;
}
@@ -0,0 +1,4 @@
{
"sourceType": "module",
"plugins": ["typescript", "classProperties"]
}
@@ -0,0 +1,11 @@
class MyClass extends BaseClass {
override show() {}

override public show() {}

override public show() {}

override size = 5;
override readonly size = 5;
override readonly size = 5;
}
2 changes: 2 additions & 0 deletions packages/babel-parser/src/parser/error-message.js
Expand Up @@ -128,6 +128,8 @@ export const ErrorMessages = makeErrorTemplates(
"constructors in/after an Optional Chain are not allowed",
OptionalChainingNoTemplate:
"Tagged Template Literals are not allowed in optionalChain",
OverrideOnConstructor:
"'override' modifier cannot appear on a constructor declaration.",
ParamDupe: "Argument name clash",
PatternHasAccessor: "Object pattern can't contain getter or setter",
PatternHasMethod: "Object pattern can't contain methods",
Expand Down
12 changes: 6 additions & 6 deletions packages/babel-parser/src/parser/statement.js
Expand Up @@ -1205,15 +1205,12 @@ export default class StatementParser extends ExpressionParser {
}
// https://tc39.es/ecma262/#prod-ClassBody
parseClassBody(
constructorAllowsSuper: boolean,
oldStrict: boolean,
): N.ClassBody {
parseClassBody(hadSuperClass: boolean, oldStrict: boolean): N.ClassBody {
this.classScope.enter();
const state: N.ParseClassMemberState = {
constructorAllowsSuper,
hadConstructor: false,
hadSuperClass,
};
let decorators: N.Decorator[] = [];
const classBody: N.ClassBody = this.startNode();
Expand Down Expand Up @@ -1400,8 +1397,11 @@ export default class StatementParser extends ExpressionParser {
if (state.hadConstructor && !this.hasPlugin("typescript")) {
this.raise(key.start, Errors.DuplicateConstructor);
}
if (isConstructor && this.hasPlugin("typescript") && member.override) {
this.raise(key.start, Errors.OverrideOnConstructor);
}
state.hadConstructor = true;
allowsDirectSuper = state.constructorAllowsSuper;
allowsDirectSuper = state.hadSuperClass;
}
this.pushClassMethod(
Expand Down
44 changes: 43 additions & 1 deletion packages/babel-parser/src/plugins/typescript/index.js
Expand Up @@ -41,6 +41,7 @@ type TsModifier =
| "abstract"
| "declare"
| "static"
| "override"
| N.Accessibility;

function nonNull<T>(x: ?T): T {
Expand Down Expand Up @@ -85,12 +86,15 @@ const TSErrors = makeErrorTemplates(
ExpectedAmbientAfterExportDeclare:
"'export declare' must be followed by an ambient declaration.",
ImportAliasHasImportType: "An import alias can not use 'import type'",
IncompatibleModifiers: "'%0' modifier cannot be used with '%1' modifier.",
IndexSignatureHasAbstract:
"Index signatures cannot have the 'abstract' modifier",
IndexSignatureHasAccessibility:
"Index signatures cannot have an accessibility modifier ('%0')",
IndexSignatureHasDeclare:
"Index signatures cannot have the 'declare' modifier",
IndexSignatureHasOverride:
"'override' modifier cannot appear on an index signature.",
IndexSignatureHasStatic:
"Index signatures cannot have the 'static' modifier",
InvalidModifierOnTypeMember:
Expand All @@ -106,6 +110,8 @@ const TSErrors = makeErrorTemplates(
"'abstract' modifier can only appear on a class, method, or property declaration.",
OptionalTypeBeforeRequired:
"A required element cannot follow an optional element.",
OverrideNotInSubClass:
"This member cannot have an 'override' modifier because its containing class does not extend another class.",
PatternIsOptional:
"A binding pattern parameter cannot be optional in an implementation signature.",
PrivateElementHasAbstract:
Expand Down Expand Up @@ -256,6 +262,16 @@ export default (superClass: Class<Parser>): Class<Parser> =>
"static",
"readonly",
);
} else if (
(modified.declare && modifier === "override") ||
(modified.override && modifier === "declare")
) {
this.raise(
startPos,
TSErrors.IncompatibleModifiers,
"declare",
"override",
);
}
modified[modifier] = true;
}
Expand Down Expand Up @@ -621,7 +637,15 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.tsParseModifiers(
node,
["readonly"],
["declare", "abstract", "private", "protected", "public", "static"],
[
"declare",
"abstract",
"private",
"protected",
"public",
"static",
"override",
],
TSErrors.InvalidModifierOnTypeMember,
);

Expand Down Expand Up @@ -2222,6 +2246,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
"private",
"public",
"protected",
"override",
]);

const callParseClassMember = () => {
Expand All @@ -2245,6 +2270,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
"readonly",
"declare",
"static",
"override",
]);

if (isStatic) {
Expand All @@ -2268,6 +2294,9 @@ export default (superClass: Class<Parser>): Class<Parser> =>
if ((member: any).declare) {
this.raise(member.start, TSErrors.IndexSignatureHasDeclare);
}
if ((member: any).override) {
this.raise(member.start, TSErrors.IndexSignatureHasOverride);
}

return;
}
Expand All @@ -2276,6 +2305,19 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.raise(member.start, TSErrors.NonAbstractClassHasAbstractMethod);
}

if ((member: any).override) {
if (isStatic) {
this.raise(
member.start,
TSErrors.IncompatibleModifiers,
"static",
"override",
);
} else if (!state.hadSuperClass) {
this.raise(member.start, TSErrors.OverrideNotInSubClass);
}
}

/*:: invariant(member.type !== "TSIndexSignature") */

super.parseClassMemberWithIsStatic(classBody, member, state, isStatic);
Expand Down
4 changes: 3 additions & 1 deletion packages/babel-parser/src/types.js
Expand Up @@ -730,6 +730,7 @@ export type ClassMemberBase = NodeBase &
computed: boolean,
// TypeScript only:
accessibility?: ?Accessibility,
override?: ?true,
abstract?: ?true,
optional?: ?true,
};
Expand Down Expand Up @@ -807,6 +808,7 @@ export type ClassPrivateProperty = NodeBase & {
optional?: true,
definite?: true,
readonly?: true,
override?: true,
};

export type OptClassDeclaration = ClassBase &
Expand Down Expand Up @@ -1536,5 +1538,5 @@ export type ParseSubscriptState = {

export type ParseClassMemberState = {|
hadConstructor: boolean,
constructorAllowsSuper: boolean,
hadSuperClass: boolean,
|};
@@ -0,0 +1,15 @@
class MyClass2 extends BaseClass {
override constructor() {}
override [x: string]: any;
override static size = 5;
static override size = 5;
}

declare class MyClass3 extends BaseClass {
declare override prop1: any
override declare prop2: any
}

class MyClass4 {
override prop: any
}

0 comments on commit 9571427

Please sign in to comment.