Skip to content

Commit

Permalink
Support TS type argument syntax in optional chaining
Browse files Browse the repository at this point in the history
Progress toward #461

The previous implemented worked for normal JS syntax, but `f?.<T>()` needs a
special case when working with tokens. Also, regular type argument syntax wasn't
marking the open-paren correctly, so this fixes that as well.
  • Loading branch information
alangpierce committed Dec 31, 2019
1 parent 12126d3 commit 5a70d14
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 2 deletions.
12 changes: 12 additions & 0 deletions src/parser/plugins/typescript.ts
Expand Up @@ -1079,6 +1079,8 @@ export function tsParseSubscript(
}
tsParseTypeArguments();
if (!noCalls && eat(tt.parenL)) {
// With f<T>(), the subscriptStartIndex marker is on the ( token.
state.tokens[state.tokens.length - 1].subscriptStartIndex = startTokenIndex;
parseCallExpressionArguments();
} else if (match(tt.backQuote)) {
// Tagged template with a type argument.
Expand All @@ -1092,6 +1094,16 @@ export function tsParseSubscript(
} else {
return;
}
} else if (!noCalls && match(tt.questionDot) && lookaheadType() === tt.lessThan) {
// If we see f?.<, then this must be an optional call with a type argument.
next();
state.tokens[startTokenIndex].isOptionalChainStart = true;
// With f?.<T>(), the subscriptStartIndex marker is on the ?. token.
state.tokens[state.tokens.length - 1].subscriptStartIndex = startTokenIndex;

tsParseTypeArguments();
expect(tt.parenL);
parseCallExpressionArguments();
}
baseParseSubscript(startTokenIndex, noCalls, stopState);
}
Expand Down
5 changes: 4 additions & 1 deletion src/transformers/OptionalChainingNullishTransformer.ts
Expand Up @@ -46,7 +46,10 @@ export default class OptionalChainingNullishTransformer extends Transformer {
} else {
arrowStartSnippet = `${param} => ${param}`;
}
if (this.tokens.matches2(tt.questionDot, tt.parenL)) {
if (
this.tokens.matches2(tt.questionDot, tt.parenL) ||
this.tokens.matches2(tt.questionDot, tt.lessThan)
) {
this.tokens.replaceTokenTrimmingLeftWhitespace(`, 'optionalCall', ${arrowStartSnippet}`);
} else if (this.tokens.matches2(tt.questionDot, tt.bracketL)) {
this.tokens.replaceTokenTrimmingLeftWhitespace(`, 'optionalAccess', ${arrowStartSnippet}`);
Expand Down
13 changes: 12 additions & 1 deletion test/typescript-test.ts
Expand Up @@ -1901,7 +1901,7 @@ describe("typescript transform", () => {
example.inner?.greet<string>()
`,
`"use strict";${OPTIONAL_CHAIN_PREFIX}
_optionalChain([example, 'access', _ => _.inner, 'optionalAccess', _2 => _2.greet()])
_optionalChain([example, 'access', _ => _.inner, 'optionalAccess', _2 => _2.greet, 'call', _3 => _3()])
`,
);
});
Expand Down Expand Up @@ -1931,4 +1931,15 @@ describe("typescript transform", () => {
`,
);
});

it("supports type arguments with optional chaining", () => {
assertTypeScriptResult(
`
const x = a.b?.<number>();
`,
`"use strict";${OPTIONAL_CHAIN_PREFIX}
const x = _optionalChain([a, 'access', _ => _.b, 'optionalCall', _2 => _2()]);
`,
);
});
});

0 comments on commit 5a70d14

Please sign in to comment.