Skip to content

Commit

Permalink
Properly remove ! from definite assignment assertions
Browse files Browse the repository at this point in the history
Fixes #639

Previously, the `!` operator for class field declarations was not treated as a
type token, so it wasn't automatically removed at transpile. In most cases, this
wasn't relevant because the class field transform removes uninitialized fields
completely. However, there are two cases where it causes an issue:
* `disableESTransforms: true`, which disables the class field transform.
* Private fields, which are skipped by the class field transform.

In both cases, we can fix the issue by just setting the `!` as a type token so
that it will naturally get removed by the TS transformer.

I also did a little refactoring to pull out the logic for handling individual
type tokens.
  • Loading branch information
alangpierce committed Oct 18, 2021
1 parent 018ee1d commit 0830175
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 6 deletions.
8 changes: 3 additions & 5 deletions src/parser/plugins/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {eat, lookaheadType, match} from "../tokenizer/index";
import {eatTypeToken, lookaheadType, match} from "../tokenizer/index";
import {TokenType as tt} from "../tokenizer/types";
import {isFlowEnabled, isTypeScriptEnabled, state} from "../traverser/base";
import {isFlowEnabled, isTypeScriptEnabled} from "../traverser/base";
import {baseParseConditional} from "../traverser/expression";
import {flowParseTypeAnnotation} from "./flow";
import {tsParseTypeAnnotation} from "./typescript";
Expand All @@ -26,9 +26,7 @@ export function typedParseConditional(noIn: boolean): void {
// Note: These "type casts" are *not* valid TS expressions.
// But we parse them here and change them when completing the arrow function.
export function typedParseParenItem(): void {
if (eat(tt.question)) {
state.tokens[state.tokens.length - 1].isType = true;
}
eatTypeToken(tt.question);
if (match(tt.colon)) {
if (isTypeScriptEnabled) {
tsParseTypeAnnotation();
Expand Down
7 changes: 7 additions & 0 deletions src/parser/tokenizer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,13 @@ export function eat(type: TokenType): boolean {
}
}

export function eatTypeToken(tokenType: TokenType): void {
const oldIsType = state.isType;
state.isType = true;
eat(tokenType);
state.isType = oldIsType;
}

export function match(type: TokenType): boolean {
return state.type === type;
}
Expand Down
3 changes: 2 additions & 1 deletion src/parser/traverser/statement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
} from "../plugins/typescript";
import {
eat,
eatTypeToken,
IdentifierRole,
lookaheadType,
lookaheadTypeAndKeyword,
Expand Down Expand Up @@ -819,7 +820,7 @@ export function parsePostMemberNameModifiers(): void {

export function parseClassProperty(): void {
if (isTypeScriptEnabled) {
eat(tt.bang);
eatTypeToken(tt.bang);
tsTryParseTypeAnnotation();
} else if (isFlowEnabled) {
if (match(tt.colon)) {
Expand Down
47 changes: 47 additions & 0 deletions test/typescript-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1074,6 +1074,22 @@ describe("typescript transform", () => {
);
});

it("handles definite assignment assertions in classes with disableESTransforms", () => {
assertTypeScriptResult(
`
class A {
foo!: number;
}
`,
`"use strict";
class A {
foo;
}
`,
{disableESTransforms: true},
);
});

it("handles definite assignment assertions on variables", () => {
assertTypeScriptResult(
`
Expand All @@ -1089,6 +1105,37 @@ describe("typescript transform", () => {
);
});

it("handles definite assignment assertions on private fields in classes", () => {
assertTypeScriptResult(
`
class A {
#a!: number;
}
`,
`"use strict";
class A {
#a;
}
`,
);
});

it("handles definite assignment assertions on private fields in classes with disableESTransforms", () => {
assertTypeScriptResult(
`
class A {
#a!: number;
}
`,
`"use strict";
class A {
#a;
}
`,
{disableESTransforms: true},
);
});

it("handles mapped type modifiers", () => {
assertTypeScriptResult(
`
Expand Down

0 comments on commit 0830175

Please sign in to comment.