Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ts] Allow ...<...> followed by newline or binary operator #14636

Merged
merged 1 commit into from Jun 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 11 additions & 20 deletions packages/babel-parser/src/plugins/typescript/index.js
Expand Up @@ -13,7 +13,6 @@ import {
type TokenType,
tokenIsTemplate,
tokenCanStartExpression,
tokenIsBinaryOperator,
} from "../../tokenizer/types";
import { types as tc } from "../../tokenizer/context";
import * as N from "../../types";
Expand Down Expand Up @@ -66,12 +65,6 @@ function assert(x: boolean): void {
}
}

function tsTokenCanStartExpression(token: TokenType) {
// tsc considers binary operators as "can start expression" tokens:
// https://github.com/microsoft/TypeScript/blob/eca1b4/src/compiler/parser.ts#L4260-L4266
return tokenCanStartExpression(token) || tokenIsBinaryOperator(token);
}

type ParsingContext =
| "EnumMembers"
| "HeritageClauseElement"
Expand Down Expand Up @@ -2429,11 +2422,11 @@ export default (superClass: Class<Parser>): Class<Parser> =>
}

const typeArguments = this.tsParseTypeArgumentsInExpression();
if (!typeArguments) throw this.unexpected();
if (!typeArguments) return;

if (isOptionalCall && !this.match(tt.parenL)) {
missingParenErrorLoc = this.state.curPosition();
throw this.unexpected();
return;
}

if (tokenIsTemplate(this.state.type)) {
Expand Down Expand Up @@ -2469,19 +2462,17 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return this.finishCallExpression(node, state.optionalChainMember);
}

// TODO: This doesn't exactly match what TS does when it comes to ASI.
// For example,
// a<b>
// if (0);
// is not valid TS code (https://github.com/microsoft/TypeScript/issues/48654)
// However, it should correctly parse anything that is correctly parsed by TS.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The linked issue has been fixed.

const tokenType = this.state.type;
if (
tsTokenCanStartExpression(this.state.type) &&
this.state.type !== tt.parenL
// a<b>>c is not (a<b>)>c, but a<(b>>c)
tokenType === tt.gt ||
// a<b>c is (a<b)>c
(tokenType !== tt.parenL &&
tokenCanStartExpression(tokenType) &&
!this.hasPrecedingLineBreak())
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

) {
// Bail out. We have something like a<b>c, which is not an expression with
// type arguments but an (a < b) > c comparison.
throw this.unexpected();
// Bail out.
return;
}

const node: N.TsInstantiationExpression = this.startNodeAt(
Expand Down
@@ -0,0 +1,40 @@
const x5 = f<true>
let yy = 0;

const x6 = f<true>
interface I {}

let x10 = f<true>
this.bar()

let x11 = f<true>
function bar() {}

let x12 = f<true>
class C {}

let x13 = f<true>
bar()

let x14 = f<true>
void bar()

class C1 {
static specialFoo = f<string>
static bar = 123
}

class C2 {
public specialFoo = f<string>
public bar = 123
}

class C3 {
private specialFoo = f<string>
private bar = 123
}

class C4 {
protected specialFoo = f<string>
protected bar = 123
}