Add rule to auto-paren optional chain in normal prop or element access (#50156)

This commit is contained in:
Ron Buckton
2022-08-03 17:17:26 -04:00
committed by GitHub
parent c82c9a9744
commit d6d26430c7
11 changed files with 158 additions and 19 deletions
+13 -13
View File
@@ -1188,7 +1188,7 @@ namespace ts {
// @api
function createDecorator(expression: Expression) {
const node = createBaseNode<Decorator>(SyntaxKind.Decorator);
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression);
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression, /*optionalChain*/ false);
node.transformFlags |=
propagateChildFlags(node.expression) |
TransformFlags.ContainsTypeScript |
@@ -2325,7 +2325,7 @@ namespace ts {
// @api
function createPropertyAccessExpression(expression: Expression, name: string | Identifier | PrivateIdentifier) {
const node = createBaseExpression<PropertyAccessExpression>(SyntaxKind.PropertyAccessExpression);
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression);
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression, /*optionalChain*/ false);
node.name = asName(name);
node.transformFlags =
propagateChildFlags(node.expression) |
@@ -2357,7 +2357,7 @@ namespace ts {
function createPropertyAccessChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, name: string | Identifier | PrivateIdentifier) {
const node = createBaseExpression<PropertyAccessChain>(SyntaxKind.PropertyAccessExpression);
node.flags |= NodeFlags.OptionalChain;
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression);
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression, /*optionalChain*/ true);
node.questionDotToken = questionDotToken;
node.name = asName(name);
node.transformFlags |=
@@ -2385,7 +2385,7 @@ namespace ts {
// @api
function createElementAccessExpression(expression: Expression, index: number | Expression) {
const node = createBaseExpression<ElementAccessExpression>(SyntaxKind.ElementAccessExpression);
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression);
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression, /*optionalChain*/ false);
node.argumentExpression = asExpression(index);
node.transformFlags |=
propagateChildFlags(node.expression) |
@@ -2415,7 +2415,7 @@ namespace ts {
function createElementAccessChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, index: number | Expression) {
const node = createBaseExpression<ElementAccessChain>(SyntaxKind.ElementAccessExpression);
node.flags |= NodeFlags.OptionalChain;
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression);
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression, /*optionalChain*/ true);
node.questionDotToken = questionDotToken;
node.argumentExpression = asExpression(index);
node.transformFlags |=
@@ -2441,7 +2441,7 @@ namespace ts {
// @api
function createCallExpression(expression: Expression, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[] | undefined) {
const node = createBaseExpression<CallExpression>(SyntaxKind.CallExpression);
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression);
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression, /*optionalChain*/ false);
node.typeArguments = asNodeArray(typeArguments);
node.arguments = parenthesizerRules().parenthesizeExpressionsOfCommaDelimitedList(createNodeArray(argumentsArray));
node.transformFlags |=
@@ -2476,7 +2476,7 @@ namespace ts {
function createCallChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[] | undefined) {
const node = createBaseExpression<CallChain>(SyntaxKind.CallExpression);
node.flags |= NodeFlags.OptionalChain;
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression);
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression, /*optionalChain*/ true);
node.questionDotToken = questionDotToken;
node.typeArguments = asNodeArray(typeArguments);
node.arguments = parenthesizerRules().parenthesizeExpressionsOfCommaDelimitedList(createNodeArray(argumentsArray));
@@ -2535,7 +2535,7 @@ namespace ts {
// @api
function createTaggedTemplateExpression(tag: Expression, typeArguments: readonly TypeNode[] | undefined, template: TemplateLiteral) {
const node = createBaseExpression<TaggedTemplateExpression>(SyntaxKind.TaggedTemplateExpression);
node.tag = parenthesizerRules().parenthesizeLeftSideOfAccess(tag);
node.tag = parenthesizerRules().parenthesizeLeftSideOfAccess(tag, /*optionalChain*/ false);
node.typeArguments = asNodeArray(typeArguments);
node.template = template;
node.transformFlags |=
@@ -3085,7 +3085,7 @@ namespace ts {
// @api
function createExpressionWithTypeArguments(expression: Expression, typeArguments: readonly TypeNode[] | undefined) {
const node = createBaseNode<ExpressionWithTypeArguments>(SyntaxKind.ExpressionWithTypeArguments);
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression);
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression, /*optionalChain*/ false);
node.typeArguments = typeArguments && parenthesizerRules().parenthesizeTypeArguments(typeArguments);
node.transformFlags |=
propagateChildFlags(node.expression) |
@@ -3125,7 +3125,7 @@ namespace ts {
// @api
function createNonNullExpression(expression: Expression) {
const node = createBaseExpression<NonNullExpression>(SyntaxKind.NonNullExpression);
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression);
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression, /*optionalChain*/ false);
node.transformFlags |=
propagateChildFlags(node.expression) |
TransformFlags.ContainsTypeScript;
@@ -3146,7 +3146,7 @@ namespace ts {
function createNonNullChain(expression: Expression) {
const node = createBaseExpression<NonNullChain>(SyntaxKind.NonNullExpression);
node.flags |= NodeFlags.OptionalChain;
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression);
node.expression = parenthesizerRules().parenthesizeLeftSideOfAccess(expression, /*optionalChain*/ true);
node.transformFlags |=
propagateChildFlags(node.expression) |
TransformFlags.ContainsTypeScript;
@@ -5824,7 +5824,7 @@ namespace ts {
}
else if (getEmitFlags(callee) & EmitFlags.HelperName) {
thisArg = createVoidZero();
target = parenthesizerRules().parenthesizeLeftSideOfAccess(callee);
target = parenthesizerRules().parenthesizeLeftSideOfAccess(callee, /*optionalChain*/ false);
}
else if (isPropertyAccessExpression(callee)) {
if (shouldBeCapturedInTempVariable(callee.expression, cacheIdentifiers)) {
@@ -5871,7 +5871,7 @@ namespace ts {
else {
// for `a()` target is `a` and thisArg is `void 0`
thisArg = createVoidZero();
target = parenthesizerRules().parenthesizeLeftSideOfAccess(expression);
target = parenthesizerRules().parenthesizeLeftSideOfAccess(expression, /*optionalChain*/ false);
}
return { target, thisArg };
+3 -2
View File
@@ -319,7 +319,7 @@ namespace ts {
* Wraps an expression in parentheses if it is needed in order to use the expression for
* property or element access.
*/
function parenthesizeLeftSideOfAccess(expression: Expression): LeftHandSideExpression {
function parenthesizeLeftSideOfAccess(expression: Expression, optionalChain?: boolean): LeftHandSideExpression {
// isLeftHandSideExpression is almost the correct criterion for when it is not necessary
// to parenthesize the expression before a dot. The known exception is:
//
@@ -328,7 +328,8 @@ namespace ts {
//
const emittedExpression = skipPartiallyEmittedExpressions(expression);
if (isLeftHandSideExpression(emittedExpression)
&& (emittedExpression.kind !== SyntaxKind.NewExpression || (emittedExpression as NewExpression).arguments)) {
&& (emittedExpression.kind !== SyntaxKind.NewExpression || (emittedExpression as NewExpression).arguments)
&& (optionalChain || !isOptionalChain(emittedExpression))) {
// TODO(rbuckton): Verify whether this assertion holds.
return expression as LeftHandSideExpression;
}
+1 -1
View File
@@ -7347,7 +7347,7 @@ namespace ts {
parenthesizeBranchOfConditionalExpression(branch: Expression): Expression;
parenthesizeExpressionOfExportDefault(expression: Expression): Expression;
parenthesizeExpressionOfNew(expression: Expression): LeftHandSideExpression;
parenthesizeLeftSideOfAccess(expression: Expression): LeftHandSideExpression;
parenthesizeLeftSideOfAccess(expression: Expression, optionalChain?: boolean): LeftHandSideExpression;
parenthesizeOperandOfPostfixUnary(operand: Expression): LeftHandSideExpression;
parenthesizeOperandOfPrefixUnary(operand: Expression): UnaryExpression;
parenthesizeExpressionsOfCommaDelimitedList(elements: readonly Expression[]): NodeArray<Expression>;
@@ -16,5 +16,5 @@ a?.b.d
//// [optionalChainWithInstantiationExpression1.js]
a?.b.d;
(a?.b).d;
a?.b.d;
@@ -10,7 +10,12 @@ const foo = new Foo();
/*a1*/(/*a2*/foo.m as any/*a3*/)/*a4*/?.();
/*b1*/(/*b2*/<any>foo.m/*b3*/)/*b4*/?.();
// https://github.com/microsoft/TypeScript/issues/50148
(foo?.m as any).length;
(<any>foo?.m).length;
(foo?.["m"] as any).length;
(<any>foo?.["m"]).length;
//// [optionalChainingInTypeAssertions.js]
var _a, _b, _c, _d;
@@ -22,3 +27,8 @@ const foo = new Foo();
(_b = foo.m) === null || _b === void 0 ? void 0 : _b.call(foo);
/*a1*/ (_c = /*a2*/ foo.m /*a3*/ /*a4*/) === null || _c === void 0 ? void 0 : _c.call(foo);
/*b1*/ (_d = /*b2*/ foo.m /*b3*/ /*b4*/) === null || _d === void 0 ? void 0 : _d.call(foo);
// https://github.com/microsoft/TypeScript/issues/50148
(foo === null || foo === void 0 ? void 0 : foo.m).length;
(foo === null || foo === void 0 ? void 0 : foo.m).length;
(foo === null || foo === void 0 ? void 0 : foo["m"]).length;
(foo === null || foo === void 0 ? void 0 : foo["m"]).length;
@@ -30,3 +30,22 @@ const foo = new Foo();
>foo : Symbol(foo, Decl(optionalChainingInTypeAssertions.ts, 4, 5))
>m : Symbol(Foo.m, Decl(optionalChainingInTypeAssertions.ts, 0, 11))
// https://github.com/microsoft/TypeScript/issues/50148
(foo?.m as any).length;
>foo?.m : Symbol(Foo.m, Decl(optionalChainingInTypeAssertions.ts, 0, 11))
>foo : Symbol(foo, Decl(optionalChainingInTypeAssertions.ts, 4, 5))
>m : Symbol(Foo.m, Decl(optionalChainingInTypeAssertions.ts, 0, 11))
(<any>foo?.m).length;
>foo?.m : Symbol(Foo.m, Decl(optionalChainingInTypeAssertions.ts, 0, 11))
>foo : Symbol(foo, Decl(optionalChainingInTypeAssertions.ts, 4, 5))
>m : Symbol(Foo.m, Decl(optionalChainingInTypeAssertions.ts, 0, 11))
(foo?.["m"] as any).length;
>foo : Symbol(foo, Decl(optionalChainingInTypeAssertions.ts, 4, 5))
>"m" : Symbol(Foo.m, Decl(optionalChainingInTypeAssertions.ts, 0, 11))
(<any>foo?.["m"]).length;
>foo : Symbol(foo, Decl(optionalChainingInTypeAssertions.ts, 4, 5))
>"m" : Symbol(Foo.m, Decl(optionalChainingInTypeAssertions.ts, 0, 11))
@@ -43,3 +43,40 @@ const foo = new Foo();
>foo : Foo
>m : () => void
// https://github.com/microsoft/TypeScript/issues/50148
(foo?.m as any).length;
>(foo?.m as any).length : any
>(foo?.m as any) : any
>foo?.m as any : any
>foo?.m : () => void
>foo : Foo
>m : () => void
>length : any
(<any>foo?.m).length;
>(<any>foo?.m).length : any
>(<any>foo?.m) : any
><any>foo?.m : any
>foo?.m : () => void
>foo : Foo
>m : () => void
>length : any
(foo?.["m"] as any).length;
>(foo?.["m"] as any).length : any
>(foo?.["m"] as any) : any
>foo?.["m"] as any : any
>foo?.["m"] : () => void
>foo : Foo
>"m" : "m"
>length : any
(<any>foo?.["m"]).length;
>(<any>foo?.["m"]).length : any
>(<any>foo?.["m"]) : any
><any>foo?.["m"] : any
>foo?.["m"] : () => void
>foo : Foo
>"m" : "m"
>length : any
@@ -10,7 +10,12 @@ const foo = new Foo();
/*a1*/(/*a2*/foo.m as any/*a3*/)/*a4*/?.();
/*b1*/(/*b2*/<any>foo.m/*b3*/)/*b4*/?.();
// https://github.com/microsoft/TypeScript/issues/50148
(foo?.m as any).length;
(<any>foo?.m).length;
(foo?.["m"] as any).length;
(<any>foo?.["m"]).length;
//// [optionalChainingInTypeAssertions.js]
class Foo {
@@ -21,3 +26,8 @@ foo.m?.();
foo.m?.();
/*a1*/ /*a2*/ foo.m /*a3*/ /*a4*/?.();
/*b1*/ /*b2*/ foo.m /*b3*/ /*b4*/?.();
// https://github.com/microsoft/TypeScript/issues/50148
(foo?.m).length;
(foo?.m).length;
(foo?.["m"]).length;
(foo?.["m"]).length;
@@ -30,3 +30,22 @@ const foo = new Foo();
>foo : Symbol(foo, Decl(optionalChainingInTypeAssertions.ts, 4, 5))
>m : Symbol(Foo.m, Decl(optionalChainingInTypeAssertions.ts, 0, 11))
// https://github.com/microsoft/TypeScript/issues/50148
(foo?.m as any).length;
>foo?.m : Symbol(Foo.m, Decl(optionalChainingInTypeAssertions.ts, 0, 11))
>foo : Symbol(foo, Decl(optionalChainingInTypeAssertions.ts, 4, 5))
>m : Symbol(Foo.m, Decl(optionalChainingInTypeAssertions.ts, 0, 11))
(<any>foo?.m).length;
>foo?.m : Symbol(Foo.m, Decl(optionalChainingInTypeAssertions.ts, 0, 11))
>foo : Symbol(foo, Decl(optionalChainingInTypeAssertions.ts, 4, 5))
>m : Symbol(Foo.m, Decl(optionalChainingInTypeAssertions.ts, 0, 11))
(foo?.["m"] as any).length;
>foo : Symbol(foo, Decl(optionalChainingInTypeAssertions.ts, 4, 5))
>"m" : Symbol(Foo.m, Decl(optionalChainingInTypeAssertions.ts, 0, 11))
(<any>foo?.["m"]).length;
>foo : Symbol(foo, Decl(optionalChainingInTypeAssertions.ts, 4, 5))
>"m" : Symbol(Foo.m, Decl(optionalChainingInTypeAssertions.ts, 0, 11))
@@ -43,3 +43,40 @@ const foo = new Foo();
>foo : Foo
>m : () => void
// https://github.com/microsoft/TypeScript/issues/50148
(foo?.m as any).length;
>(foo?.m as any).length : any
>(foo?.m as any) : any
>foo?.m as any : any
>foo?.m : () => void
>foo : Foo
>m : () => void
>length : any
(<any>foo?.m).length;
>(<any>foo?.m).length : any
>(<any>foo?.m) : any
><any>foo?.m : any
>foo?.m : () => void
>foo : Foo
>m : () => void
>length : any
(foo?.["m"] as any).length;
>(foo?.["m"] as any).length : any
>(foo?.["m"] as any) : any
>foo?.["m"] as any : any
>foo?.["m"] : () => void
>foo : Foo
>"m" : "m"
>length : any
(<any>foo?.["m"]).length;
>(<any>foo?.["m"]).length : any
>(<any>foo?.["m"]) : any
><any>foo?.["m"] : any
>foo?.["m"] : () => void
>foo : Foo
>"m" : "m"
>length : any
@@ -11,3 +11,9 @@ const foo = new Foo();
/*a1*/(/*a2*/foo.m as any/*a3*/)/*a4*/?.();
/*b1*/(/*b2*/<any>foo.m/*b3*/)/*b4*/?.();
// https://github.com/microsoft/TypeScript/issues/50148
(foo?.m as any).length;
(<any>foo?.m).length;
(foo?.["m"] as any).length;
(<any>foo?.["m"]).length;