Skip to content

Commit

Permalink
Fix parse failures when using ?? just after the end of a type (#721)
Browse files Browse the repository at this point in the history
This was detected when running Sucrase on the Babel test suite.
  • Loading branch information
alangpierce authored Jul 14, 2022
1 parent d304d24 commit f9aeb43
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 6 deletions.
2 changes: 0 additions & 2 deletions spec-compliance-tests/babel-tests/check-babel-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ flow/scope/declare-module
flow/this-annotation/function-type
flow/typecasts/yield
jsx/basic/3
typescript/cast/as
typescript/export/as-namespace
typescript/import/export-import
typescript/import/export-import-require
Expand All @@ -76,7 +75,6 @@ typescript/import/export-import-type-require
typescript/import/import-default-id-type
typescript/import/type-asi
typescript/import/type-equals-require
typescript/type-arguments/instantiation-expression-binary-operator
`
.split("\n")
.filter((s) => s);
Expand Down
3 changes: 3 additions & 0 deletions src/parser/plugins/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1242,6 +1242,9 @@ export function tsParseSubscript(
// Tagged template with a type argument.
parseTemplate();
} else if (
// The remaining possible case is an instantiation expression, e.g.
// Array<number> . Check for a few cases that would disqualify it and
// cause us to bail out.
// a<b>>c is not (a<b>)>c, but a<(b>>c)
state.type === tt.greaterThan ||
// a<b>c is (a<b)>c
Expand Down
9 changes: 7 additions & 2 deletions src/parser/tokenizer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ function readToken_gt(): void {

/**
* Called after `as` expressions in TS; we're switching from a type to a
* non-type context, so a > token may actually be >=
* non-type context, so a > token may actually be >= .
*/
export function rescan_gt(): void {
if (state.type === tt.greaterThan) {
Expand Down Expand Up @@ -565,7 +565,12 @@ function readToken_question(): void {
// '?'
const nextChar = input.charCodeAt(state.pos + 1);
const nextChar2 = input.charCodeAt(state.pos + 2);
if (nextChar === charCodes.questionMark && !state.isType) {
if (
nextChar === charCodes.questionMark &&
// In Flow (but not TypeScript), ??string is a valid type that should be
// tokenized as two individual ? tokens.
!(isFlowEnabled && state.isType)
) {
if (nextChar2 === charCodes.equalsTo) {
// '??='
finishOp(tt.assign, 3);
Expand Down
11 changes: 11 additions & 0 deletions test/flow-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -688,4 +688,15 @@ describe("transform flow", () => {
`,
);
});

it("handles two ? operators in a row", () => {
assertFlowResult(
`
type T = ??number;
`,
`"use strict";
`,
);
});
});
19 changes: 17 additions & 2 deletions test/typescript-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
IMPORT_DEFAULT_PREFIX,
IMPORT_WILDCARD_PREFIX,
JSX_PREFIX,
NULLISH_COALESCE_PREFIX,
OPTIONAL_CHAIN_PREFIX,
} from "./prefixes";
import {assertResult, devProps} from "./util";
Expand Down Expand Up @@ -2388,15 +2389,17 @@ describe("typescript transform", () => {
);
});

it("properly handles a >= symbol after an `as` cast", () => {
it("properly handles >= and ?? after `as`", () => {
assertTypeScriptResult(
`
const x: string | number = 1;
if (x as number >= 5) {}
if (y as unknown ?? false) {}
`,
`"use strict";
`"use strict";${NULLISH_COALESCE_PREFIX}
const x = 1;
if (x >= 5) {}
if (_nullishCoalesce(y , () => ( false))) {}
`,
);
});
Expand Down Expand Up @@ -3214,6 +3217,18 @@ describe("typescript transform", () => {
);
});

it("allows instantiation expressions followed by ??", () => {
assertResult(
`
const foo = a<b> ?? c;
`,
`${NULLISH_COALESCE_PREFIX}
const foo = _nullishCoalesce(a, () => ( c));
`,
{transforms: ["typescript"]},
);
});

it("allows extends constraints on infer type variables", () => {
assertResult(
`
Expand Down

0 comments on commit f9aeb43

Please sign in to comment.