Skip to content

Commit

Permalink
[ts] Support const modifier in type parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolo-ribaudo committed Feb 7, 2023
1 parent 70e0974 commit 2f89c76
Show file tree
Hide file tree
Showing 16 changed files with 1,643 additions and 87 deletions.
202 changes: 115 additions & 87 deletions packages/babel-parser/src/plugins/typescript/index.ts
Expand Up @@ -52,6 +52,7 @@ type TsModifier =
| "declare"
| "static"
| "override"
| "const"
| N.Accessibility
| N.VarianceAnnotations;

Expand Down Expand Up @@ -324,7 +325,11 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
allowedModifiers: T[],
stopOnStartOfClassStaticBlock?: boolean,
): T | undefined | null {
if (!tokenIsIdentifier(this.state.type) && this.state.type !== tt._in) {
if (
!tokenIsIdentifier(this.state.type) &&
this.state.type !== tt._in &&
this.state.type !== tt._const
) {
return undefined;
}

Expand All @@ -345,20 +350,20 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
* this.tsParseModifiers({ modified: node, allowedModifiers: ["public"] });
* this.tsParseModifiers({ modified: node, allowedModifiers: ["abstract", "readonly"] });
*/
tsParseModifiers({
modified,
allowedModifiers,
disallowedModifiers,
stopOnStartOfClassStaticBlock,
errorTemplate = TSErrors.InvalidModifierOnTypeMember,
}: {
modified: ModifierBase;
allowedModifiers: readonly TsModifier[];
disallowedModifiers?: TsModifier[];
stopOnStartOfClassStaticBlock?: boolean;
// FIXME: make sure errorTemplate can receive `modifier`
errorTemplate?: any;
}): void {
tsParseModifiers<N extends ModifierBase>(
{
allowedModifiers,
disallowedModifiers,
stopOnStartOfClassStaticBlock,
errorTemplate = TSErrors.InvalidModifierOnTypeMember,
}: {
allowedModifiers: readonly TsModifier[];
disallowedModifiers?: TsModifier[];
stopOnStartOfClassStaticBlock?: boolean;
errorTemplate?: typeof TSErrors.InvalidModifierOnTypeMember;
},
modified: N,
): void {
const enforceOrder = (
loc: Position,
modifier: TsModifier,
Expand Down Expand Up @@ -644,37 +649,44 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
return this.finishNode(node, "TSTypeQuery");
}

tsParseInOutModifiers(node: N.TsTypeParameter) {
this.tsParseModifiers({
modified: node,
allowedModifiers: ["in", "out"],
disallowedModifiers: [
"public",
"private",
"protected",
"readonly",
"declare",
"abstract",
"override",
],
errorTemplate: TSErrors.InvalidModifierOnTypeParameter,
});
}

// for better error recover
tsParseNoneModifiers(node: N.TsTypeParameter) {
this.tsParseModifiers({
modified: node,
allowedModifiers: [],
disallowedModifiers: ["in", "out"],
errorTemplate: TSErrors.InvalidModifierOnTypeParameterPositions,
});
}
tsParseInOutModifiers = this.tsParseModifiers.bind(this, {
allowedModifiers: ["in", "out"],
disallowedModifiers: [
"const",
"public",
"private",
"protected",
"readonly",
"declare",
"abstract",
"override",
],
errorTemplate: TSErrors.InvalidModifierOnTypeParameter,
});

tsParseConstModifier = this.tsParseModifiers.bind(this, {
allowedModifiers: ["const"],
// for better error recover
disallowedModifiers: ["in", "out"],
errorTemplate: TSErrors.InvalidModifierOnTypeParameterPositions,
});

tsParseInOutConstModifiers = this.tsParseModifiers.bind(this, {
allowedModifiers: ["in", "out", "const"],
disallowedModifiers: [
"public",
"private",
"protected",
"readonly",
"declare",
"abstract",
"override",
],
errorTemplate: TSErrors.InvalidModifierOnTypeParameter,
});

tsParseTypeParameter(
parseModifiers: (
node: Undone<N.TsTypeParameter>,
) => void = this.tsParseNoneModifiers.bind(this),
parseModifiers: (node: Undone<N.TsTypeParameter>) => void,
): N.TsTypeParameter {
const node = this.startNode<N.TsTypeParameter>();

Expand All @@ -687,16 +699,14 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
}

tsTryParseTypeParameters(
parseModifiers?: ((node: N.TsTypeParameter) => void) | null,
parseModifiers: (node: N.TsTypeParameter) => void,
): N.TsTypeParameterDeclaration | undefined | null {
if (this.match(tt.lt)) {
return this.tsParseTypeParameters(parseModifiers);
}
}

tsParseTypeParameters(
parseModifiers?: ((node: N.TsTypeParameter) => void) | null,
) {
tsParseTypeParameters(parseModifiers: (node: N.TsTypeParameter) => void) {
const node = this.startNode<N.TsTypeParameterDeclaration>();

if (this.match(tt.lt) || this.match(tt.jsxTagStart)) {
Expand Down Expand Up @@ -739,7 +749,9 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
? "returnType"
: "typeAnnotation";

signature.typeParameters = this.tsTryParseTypeParameters();
signature.typeParameters = this.tsTryParseTypeParameters(
this.tsParseConstModifier,
);
this.expect(tt.parenL);
signature[paramsKey] = this.tsParseBindingListForSignature();
if (returnTokenRequired) {
Expand Down Expand Up @@ -922,19 +934,21 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
}
}

this.tsParseModifiers({
modified: node,
allowedModifiers: ["readonly"],
disallowedModifiers: [
"declare",
"abstract",
"private",
"protected",
"public",
"static",
"override",
],
});
this.tsParseModifiers(
{
allowedModifiers: ["readonly"],
disallowedModifiers: [
"declare",
"abstract",
"private",
"protected",
"public",
"static",
"override",
],
},
node,
);

const idx = this.tsTryParseIndexSignature(node);
if (idx) {
Expand Down Expand Up @@ -1699,7 +1713,7 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
}

node.typeParameters = this.tsTryParseTypeParameters(
this.tsParseInOutModifiers.bind(this),
this.tsParseInOutConstModifiers,
);
if (this.eat(tt._extends)) {
node.extends = this.tsParseHeritageClause("extends");
Expand All @@ -1718,7 +1732,7 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>

node.typeAnnotation = this.tsInType(() => {
node.typeParameters = this.tsTryParseTypeParameters(
this.tsParseInOutModifiers.bind(this),
this.tsParseInOutModifiers,
);

this.expect(tt.eq);
Expand Down Expand Up @@ -2186,7 +2200,9 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
const res: Undone<N.ArrowFunctionExpression> | undefined | null =
this.tsTryParseAndCatch(() => {
const node = this.startNodeAt<N.ArrowFunctionExpression>(startLoc);
node.typeParameters = this.tsParseTypeParameters();
node.typeParameters = this.tsParseTypeParameters(
this.tsParseConstModifier,
);
// Don't use overloaded parseFunctionParams which would look for "<" again.
super.parseFunctionParams(node);
node.returnType = this.tsTryParseTypeOrTypePredicateAnnotation();
Expand Down Expand Up @@ -2260,16 +2276,18 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
let override = false;
if (allowModifiers !== undefined) {
const modified: ModifierBase = {};
this.tsParseModifiers({
this.tsParseModifiers(
{
allowedModifiers: [
"public",
"private",
"protected",
"override",
"readonly",
],
},
modified,
allowedModifiers: [
"public",
"private",
"protected",
"override",
"readonly",
],
});
);
accessibility = modified.accessibility;
override = modified.override;
readonly = modified.readonly;
Expand Down Expand Up @@ -2852,13 +2870,15 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
"readonly",
"static",
] as const;
this.tsParseModifiers({
modified: member,
allowedModifiers: modifiers,
disallowedModifiers: ["in", "out"],
stopOnStartOfClassStaticBlock: true,
errorTemplate: TSErrors.InvalidModifierOnTypeParameterPositions,
});
this.tsParseModifiers(
{
allowedModifiers: modifiers,
disallowedModifiers: ["in", "out"],
stopOnStartOfClassStaticBlock: true,
errorTemplate: TSErrors.InvalidModifierOnTypeParameterPositions,
},
member,
);

const callParseClassMemberWithIsStatic = () => {
if (this.tsIsStartOfStaticBlocks()) {
Expand Down Expand Up @@ -3104,7 +3124,7 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
(node as any).declare ? BIND_TS_AMBIENT : BIND_CLASS,
);
const typeParameters = this.tsTryParseTypeParameters(
this.tsParseInOutModifiers.bind(this),
this.tsParseInOutConstModifiers,
);
if (typeParameters) node.typeParameters = typeParameters;
}
Expand Down Expand Up @@ -3189,7 +3209,9 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
isConstructor: boolean,
allowsDirectSuper: boolean,
): void {
const typeParameters = this.tsTryParseTypeParameters();
const typeParameters = this.tsTryParseTypeParameters(
this.tsParseConstModifier,
);
if (typeParameters && isConstructor) {
this.raise(TSErrors.ConstructorHasTypeParameters, {
at: typeParameters,
Expand Down Expand Up @@ -3219,7 +3241,9 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
isGenerator: boolean,
isAsync: boolean,
): void {
const typeParameters = this.tsTryParseTypeParameters();
const typeParameters = this.tsTryParseTypeParameters(
this.tsParseConstModifier,
);
if (typeParameters) method.typeParameters = typeParameters;
super.pushClassPrivateMethod(classBody, method, isGenerator, isAsync);
}
Expand Down Expand Up @@ -3256,7 +3280,9 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
isAccessor: boolean,
refExpressionErrors?: ExpressionErrors | null,
) {
const typeParameters = this.tsTryParseTypeParameters();
const typeParameters = this.tsTryParseTypeParameters(
this.tsParseConstModifier,
);
if (typeParameters) prop.typeParameters = typeParameters;

return super.parseObjPropValue(
Expand All @@ -3272,7 +3298,9 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
}

parseFunctionParams(node: N.Function, allowModifiers?: boolean): void {
const typeParameters = this.tsTryParseTypeParameters();
const typeParameters = this.tsTryParseTypeParameters(
this.tsParseConstModifier,
);
if (typeParameters) node.typeParameters = typeParameters;
super.parseFunctionParams(node, allowModifiers);
}
Expand Down Expand Up @@ -3359,7 +3387,7 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
let typeParameters: N.TsTypeParameterDeclaration | undefined | null;
const arrow = this.tryParse(abort => {
// This is similar to TypeScript's `tryParseParenthesizedArrowFunctionExpression`.
typeParameters = this.tsParseTypeParameters();
typeParameters = this.tsParseTypeParameters(this.tsParseConstModifier);
const expr = super.parseMaybeAssign(
refExpressionErrors,
afterLeftParse,
Expand Down
1 change: 1 addition & 0 deletions packages/babel-parser/src/types.d.ts
Expand Up @@ -1053,6 +1053,7 @@ export interface TsTypeParameter extends NodeBase {
name: string | Identifier;
in?: boolean;
out?: boolean;
const?: boolean;
constraint?: TsType;
default?: TsType;
}
Expand Down
@@ -0,0 +1,24 @@
function a<const T>() {}
function b<const T extends U>() {}
function c<T, const U>() {}
declare function d<const T>();
<const T>() => {};
<const T extends U>() => {};

class A<const T> {}
class B<const T extends U> {}
class C<T, const U> {}
class D<in const T> {}
class E<const in T> {}

interface I<const T> {}
interface J<const T extends U> {}
interface K<T, const U> {}
interface L<in const T> {}
interface M<const in T> {}

class _ {
method<const T>() {}
method<const T extends U>() {}
method<T, const U>() {}
}
@@ -0,0 +1,3 @@
{
"BABEL_8_BREAKING": false
}

0 comments on commit 2f89c76

Please sign in to comment.