• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/* @internal */
2namespace ts {
3    export function createParenthesizerRules(factory: NodeFactory): ParenthesizerRules {
4        interface BinaryPlusExpression extends BinaryExpression {
5            cachedLiteralKind: SyntaxKind;
6        }
7
8        let binaryLeftOperandParenthesizerCache: ESMap<BinaryOperator, (node: Expression) => Expression> | undefined;
9        let binaryRightOperandParenthesizerCache: ESMap<BinaryOperator, (node: Expression) => Expression> | undefined;
10
11        return {
12            getParenthesizeLeftSideOfBinaryForOperator,
13            getParenthesizeRightSideOfBinaryForOperator,
14            parenthesizeLeftSideOfBinary,
15            parenthesizeRightSideOfBinary,
16            parenthesizeExpressionOfComputedPropertyName,
17            parenthesizeConditionOfConditionalExpression,
18            parenthesizeBranchOfConditionalExpression,
19            parenthesizeExpressionOfExportDefault,
20            parenthesizeExpressionOfNew,
21            parenthesizeLeftSideOfAccess,
22            parenthesizeOperandOfPostfixUnary,
23            parenthesizeOperandOfPrefixUnary,
24            parenthesizeExpressionsOfCommaDelimitedList,
25            parenthesizeExpressionForDisallowedComma,
26            parenthesizeExpressionOfExpressionStatement,
27            parenthesizeConciseBodyOfArrowFunction,
28            parenthesizeCheckTypeOfConditionalType,
29            parenthesizeExtendsTypeOfConditionalType,
30            parenthesizeConstituentTypesOfUnionType,
31            parenthesizeConstituentTypeOfUnionType,
32            parenthesizeConstituentTypesOfIntersectionType,
33            parenthesizeConstituentTypeOfIntersectionType,
34            parenthesizeOperandOfTypeOperator,
35            parenthesizeOperandOfReadonlyTypeOperator,
36            parenthesizeNonArrayTypeOfPostfixType,
37            parenthesizeElementTypesOfTupleType,
38            parenthesizeElementTypeOfTupleType,
39            parenthesizeTypeOfOptionalType,
40            parenthesizeTypeArguments,
41            parenthesizeLeadingTypeArgument,
42        };
43
44        function getParenthesizeLeftSideOfBinaryForOperator(operatorKind: BinaryOperator) {
45            binaryLeftOperandParenthesizerCache ||= new Map();
46            let parenthesizerRule = binaryLeftOperandParenthesizerCache.get(operatorKind);
47            if (!parenthesizerRule) {
48                parenthesizerRule = node => parenthesizeLeftSideOfBinary(operatorKind, node);
49                binaryLeftOperandParenthesizerCache.set(operatorKind, parenthesizerRule);
50            }
51            return parenthesizerRule;
52        }
53
54        function getParenthesizeRightSideOfBinaryForOperator(operatorKind: BinaryOperator) {
55            binaryRightOperandParenthesizerCache ||= new Map();
56            let parenthesizerRule = binaryRightOperandParenthesizerCache.get(operatorKind);
57            if (!parenthesizerRule) {
58                parenthesizerRule = node => parenthesizeRightSideOfBinary(operatorKind, /*leftSide*/ undefined, node);
59                binaryRightOperandParenthesizerCache.set(operatorKind, parenthesizerRule);
60            }
61            return parenthesizerRule;
62        }
63
64        /**
65         * Determines whether the operand to a BinaryExpression needs to be parenthesized.
66         *
67         * @param binaryOperator The operator for the BinaryExpression.
68         * @param operand The operand for the BinaryExpression.
69         * @param isLeftSideOfBinary A value indicating whether the operand is the left side of the
70         *                           BinaryExpression.
71         */
72        function binaryOperandNeedsParentheses(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean, leftOperand: Expression | undefined) {
73            // If the operand has lower precedence, then it needs to be parenthesized to preserve the
74            // intent of the expression. For example, if the operand is `a + b` and the operator is
75            // `*`, then we need to parenthesize the operand to preserve the intended order of
76            // operations: `(a + b) * x`.
77            //
78            // If the operand has higher precedence, then it does not need to be parenthesized. For
79            // example, if the operand is `a * b` and the operator is `+`, then we do not need to
80            // parenthesize to preserve the intended order of operations: `a * b + x`.
81            //
82            // If the operand has the same precedence, then we need to check the associativity of
83            // the operator based on whether this is the left or right operand of the expression.
84            //
85            // For example, if `a / d` is on the right of operator `*`, we need to parenthesize
86            // to preserve the intended order of operations: `x * (a / d)`
87            //
88            // If `a ** d` is on the left of operator `**`, we need to parenthesize to preserve
89            // the intended order of operations: `(a ** b) ** c`
90            const binaryOperatorPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, binaryOperator);
91            const binaryOperatorAssociativity = getOperatorAssociativity(SyntaxKind.BinaryExpression, binaryOperator);
92            const emittedOperand = skipPartiallyEmittedExpressions(operand);
93            if (!isLeftSideOfBinary && operand.kind === SyntaxKind.ArrowFunction && binaryOperatorPrecedence > OperatorPrecedence.Assignment) {
94                // We need to parenthesize arrow functions on the right side to avoid it being
95                // parsed as parenthesized expression: `a && (() => {})`
96                return true;
97            }
98            const operandPrecedence = getExpressionPrecedence(emittedOperand);
99            switch (compareValues(operandPrecedence, binaryOperatorPrecedence)) {
100                case Comparison.LessThan:
101                    // If the operand is the right side of a right-associative binary operation
102                    // and is a yield expression, then we do not need parentheses.
103                    if (!isLeftSideOfBinary
104                        && binaryOperatorAssociativity === Associativity.Right
105                        && operand.kind === SyntaxKind.YieldExpression) {
106                        return false;
107                    }
108
109                    return true;
110
111                case Comparison.GreaterThan:
112                    return false;
113
114                case Comparison.EqualTo:
115                    if (isLeftSideOfBinary) {
116                        // No need to parenthesize the left operand when the binary operator is
117                        // left associative:
118                        //  (a*b)/x    -> a*b/x
119                        //  (a**b)/x   -> a**b/x
120                        //
121                        // Parentheses are needed for the left operand when the binary operator is
122                        // right associative:
123                        //  (a/b)**x   -> (a/b)**x
124                        //  (a**b)**x  -> (a**b)**x
125                        return binaryOperatorAssociativity === Associativity.Right;
126                    }
127                    else {
128                        if (isBinaryExpression(emittedOperand)
129                            && emittedOperand.operatorToken.kind === binaryOperator) {
130                            // No need to parenthesize the right operand when the binary operator and
131                            // operand are the same and one of the following:
132                            //  x*(a*b)     => x*a*b
133                            //  x|(a|b)     => x|a|b
134                            //  x&(a&b)     => x&a&b
135                            //  x^(a^b)     => x^a^b
136                            if (operatorHasAssociativeProperty(binaryOperator)) {
137                                return false;
138                            }
139
140                            // No need to parenthesize the right operand when the binary operator
141                            // is plus (+) if both the left and right operands consist solely of either
142                            // literals of the same kind or binary plus (+) expressions for literals of
143                            // the same kind (recursively).
144                            //  "a"+(1+2)       => "a"+(1+2)
145                            //  "a"+("b"+"c")   => "a"+"b"+"c"
146                            if (binaryOperator === SyntaxKind.PlusToken) {
147                                const leftKind = leftOperand ? getLiteralKindOfBinaryPlusOperand(leftOperand) : SyntaxKind.Unknown;
148                                if (isLiteralKind(leftKind) && leftKind === getLiteralKindOfBinaryPlusOperand(emittedOperand)) {
149                                    return false;
150                                }
151                            }
152                        }
153
154                        // No need to parenthesize the right operand when the operand is right
155                        // associative:
156                        //  x/(a**b)    -> x/a**b
157                        //  x**(a**b)   -> x**a**b
158                        //
159                        // Parentheses are needed for the right operand when the operand is left
160                        // associative:
161                        //  x/(a*b)     -> x/(a*b)
162                        //  x**(a/b)    -> x**(a/b)
163                        const operandAssociativity = getExpressionAssociativity(emittedOperand);
164                        return operandAssociativity === Associativity.Left;
165                    }
166            }
167        }
168
169        /**
170         * Determines whether a binary operator is mathematically associative.
171         *
172         * @param binaryOperator The binary operator.
173         */
174        function operatorHasAssociativeProperty(binaryOperator: SyntaxKind) {
175            // The following operators are associative in JavaScript:
176            //  (a*b)*c     -> a*(b*c)  -> a*b*c
177            //  (a|b)|c     -> a|(b|c)  -> a|b|c
178            //  (a&b)&c     -> a&(b&c)  -> a&b&c
179            //  (a^b)^c     -> a^(b^c)  -> a^b^c
180            //  (a,b),c     -> a,(b,c)  -> a,b,c
181            //
182            // While addition is associative in mathematics, JavaScript's `+` is not
183            // guaranteed to be associative as it is overloaded with string concatenation.
184            return binaryOperator === SyntaxKind.AsteriskToken
185                || binaryOperator === SyntaxKind.BarToken
186                || binaryOperator === SyntaxKind.AmpersandToken
187                || binaryOperator === SyntaxKind.CaretToken
188                || binaryOperator === SyntaxKind.CommaToken;
189        }
190
191        /**
192         * This function determines whether an expression consists of a homogeneous set of
193         * literal expressions or binary plus expressions that all share the same literal kind.
194         * It is used to determine whether the right-hand operand of a binary plus expression can be
195         * emitted without parentheses.
196         */
197        function getLiteralKindOfBinaryPlusOperand(node: Expression): SyntaxKind {
198            node = skipPartiallyEmittedExpressions(node);
199
200            if (isLiteralKind(node.kind)) {
201                return node.kind;
202            }
203
204            if (node.kind === SyntaxKind.BinaryExpression && (node as BinaryExpression).operatorToken.kind === SyntaxKind.PlusToken) {
205                if ((node as BinaryPlusExpression).cachedLiteralKind !== undefined) {
206                    return (node as BinaryPlusExpression).cachedLiteralKind;
207                }
208
209                const leftKind = getLiteralKindOfBinaryPlusOperand((node as BinaryExpression).left);
210                const literalKind = isLiteralKind(leftKind)
211                    && leftKind === getLiteralKindOfBinaryPlusOperand((node as BinaryExpression).right)
212                        ? leftKind
213                        : SyntaxKind.Unknown;
214
215                (node as BinaryPlusExpression).cachedLiteralKind = literalKind;
216                return literalKind;
217            }
218
219            return SyntaxKind.Unknown;
220        }
221
222        /**
223         * Wraps the operand to a BinaryExpression in parentheses if they are needed to preserve the intended
224         * order of operations.
225         *
226         * @param binaryOperator The operator for the BinaryExpression.
227         * @param operand The operand for the BinaryExpression.
228         * @param isLeftSideOfBinary A value indicating whether the operand is the left side of the
229         *                           BinaryExpression.
230         */
231        function parenthesizeBinaryOperand(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean, leftOperand?: Expression) {
232            const skipped = skipPartiallyEmittedExpressions(operand);
233
234            // If the resulting expression is already parenthesized, we do not need to do any further processing.
235            if (skipped.kind === SyntaxKind.ParenthesizedExpression) {
236                return operand;
237            }
238
239            return binaryOperandNeedsParentheses(binaryOperator, operand, isLeftSideOfBinary, leftOperand)
240                ? factory.createParenthesizedExpression(operand)
241                : operand;
242        }
243
244
245        function parenthesizeLeftSideOfBinary(binaryOperator: SyntaxKind, leftSide: Expression): Expression {
246            return parenthesizeBinaryOperand(binaryOperator, leftSide, /*isLeftSideOfBinary*/ true);
247        }
248
249        function parenthesizeRightSideOfBinary(binaryOperator: SyntaxKind, leftSide: Expression | undefined, rightSide: Expression): Expression {
250            return parenthesizeBinaryOperand(binaryOperator, rightSide, /*isLeftSideOfBinary*/ false, leftSide);
251        }
252
253        function parenthesizeExpressionOfComputedPropertyName(expression: Expression): Expression {
254            return isCommaSequence(expression) ? factory.createParenthesizedExpression(expression) : expression;
255        }
256
257        function parenthesizeConditionOfConditionalExpression(condition: Expression): Expression {
258            const conditionalPrecedence = getOperatorPrecedence(SyntaxKind.ConditionalExpression, SyntaxKind.QuestionToken);
259            const emittedCondition = skipPartiallyEmittedExpressions(condition);
260            const conditionPrecedence = getExpressionPrecedence(emittedCondition);
261            if (compareValues(conditionPrecedence, conditionalPrecedence) !== Comparison.GreaterThan) {
262                return factory.createParenthesizedExpression(condition);
263            }
264            return condition;
265        }
266
267        function parenthesizeBranchOfConditionalExpression(branch: Expression): Expression {
268            // per ES grammar both 'whenTrue' and 'whenFalse' parts of conditional expression are assignment expressions
269            // so in case when comma expression is introduced as a part of previous transformations
270            // if should be wrapped in parens since comma operator has the lowest precedence
271            const emittedExpression = skipPartiallyEmittedExpressions(branch);
272            return isCommaSequence(emittedExpression)
273                ? factory.createParenthesizedExpression(branch)
274                : branch;
275        }
276
277        /**
278         *  [Per the spec](https://tc39.github.io/ecma262/#prod-ExportDeclaration), `export default` accepts _AssigmentExpression_ but
279         *  has a lookahead restriction for `function`, `async function`, and `class`.
280         *
281         * Basically, that means we need to parenthesize in the following cases:
282         *
283         * - BinaryExpression of CommaToken
284         * - CommaList (synthetic list of multiple comma expressions)
285         * - FunctionExpression
286         * - ClassExpression
287         */
288        function parenthesizeExpressionOfExportDefault(expression: Expression): Expression {
289            const check = skipPartiallyEmittedExpressions(expression);
290            let needsParens = isCommaSequence(check);
291            if (!needsParens) {
292                switch (getLeftmostExpression(check, /*stopAtCallExpression*/ false).kind) {
293                    case SyntaxKind.ClassExpression:
294                    case SyntaxKind.FunctionExpression:
295                        needsParens = true;
296                }
297            }
298            return needsParens ? factory.createParenthesizedExpression(expression) : expression;
299        }
300
301        /**
302         * Wraps an expression in parentheses if it is needed in order to use the expression
303         * as the expression of a `NewExpression` node.
304         */
305        function parenthesizeExpressionOfNew(expression: Expression): LeftHandSideExpression {
306            const leftmostExpr = getLeftmostExpression(expression, /*stopAtCallExpressions*/ true);
307            switch (leftmostExpr.kind) {
308                case SyntaxKind.CallExpression:
309                    return factory.createParenthesizedExpression(expression);
310
311                case SyntaxKind.NewExpression:
312                    return !(leftmostExpr as NewExpression).arguments
313                        ? factory.createParenthesizedExpression(expression)
314                        : expression as LeftHandSideExpression; // TODO(rbuckton): Verify this assertion holds
315            }
316
317            return parenthesizeLeftSideOfAccess(expression);
318        }
319
320        /**
321         * Wraps an expression in parentheses if it is needed in order to use the expression for
322         * property or element access.
323         */
324        function parenthesizeLeftSideOfAccess(expression: Expression, optionalChain?: boolean): LeftHandSideExpression {
325            // isLeftHandSideExpression is almost the correct criterion for when it is not necessary
326            // to parenthesize the expression before a dot. The known exception is:
327            //
328            //    NewExpression:
329            //       new C.x        -> not the same as (new C).x
330            //
331            const emittedExpression = skipPartiallyEmittedExpressions(expression);
332            if (isLeftHandSideExpression(emittedExpression)
333                && (emittedExpression.kind !== SyntaxKind.NewExpression || (emittedExpression as NewExpression).arguments)
334                && (optionalChain || !isOptionalChain(emittedExpression))) {
335                // TODO(rbuckton): Verify whether this assertion holds.
336                return expression as LeftHandSideExpression;
337            }
338
339            // TODO(rbuckton): Verifiy whether `setTextRange` is needed.
340            return setTextRange(factory.createParenthesizedExpression(expression), expression);
341        }
342
343        function parenthesizeOperandOfPostfixUnary(operand: Expression): LeftHandSideExpression {
344            // TODO(rbuckton): Verifiy whether `setTextRange` is needed.
345            return isLeftHandSideExpression(operand) ? operand : setTextRange(factory.createParenthesizedExpression(operand), operand);
346        }
347
348        function parenthesizeOperandOfPrefixUnary(operand: Expression): UnaryExpression {
349            // TODO(rbuckton): Verifiy whether `setTextRange` is needed.
350            return isUnaryExpression(operand) ? operand : setTextRange(factory.createParenthesizedExpression(operand), operand);
351        }
352
353        function parenthesizeExpressionsOfCommaDelimitedList(elements: NodeArray<Expression>): NodeArray<Expression> {
354            const result = sameMap(elements, parenthesizeExpressionForDisallowedComma);
355            return setTextRange(factory.createNodeArray(result, elements.hasTrailingComma), elements);
356        }
357
358        function parenthesizeExpressionForDisallowedComma(expression: Expression): Expression {
359            const emittedExpression = skipPartiallyEmittedExpressions(expression);
360            const expressionPrecedence = getExpressionPrecedence(emittedExpression);
361            const commaPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, SyntaxKind.CommaToken);
362            // TODO(rbuckton): Verifiy whether `setTextRange` is needed.
363            return expressionPrecedence > commaPrecedence ? expression : setTextRange(factory.createParenthesizedExpression(expression), expression);
364        }
365
366        function parenthesizeExpressionOfExpressionStatement(expression: Expression): Expression {
367            const emittedExpression = skipPartiallyEmittedExpressions(expression);
368            if (isCallExpression(emittedExpression)) {
369                const callee = emittedExpression.expression;
370                const kind = skipPartiallyEmittedExpressions(callee).kind;
371                if (kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction) {
372                    // TODO(rbuckton): Verifiy whether `setTextRange` is needed.
373                    const updated = factory.updateCallExpression(
374                        emittedExpression,
375                        setTextRange(factory.createParenthesizedExpression(callee), callee),
376                        emittedExpression.typeArguments,
377                        emittedExpression.arguments
378                    );
379                    return factory.restoreOuterExpressions(expression, updated, OuterExpressionKinds.PartiallyEmittedExpressions);
380                }
381            }
382
383            const leftmostExpressionKind = getLeftmostExpression(emittedExpression, /*stopAtCallExpressions*/ false).kind;
384            if (leftmostExpressionKind === SyntaxKind.ObjectLiteralExpression || leftmostExpressionKind === SyntaxKind.FunctionExpression) {
385                // TODO(rbuckton): Verifiy whether `setTextRange` is needed.
386                return setTextRange(factory.createParenthesizedExpression(expression), expression);
387            }
388
389            return expression;
390        }
391
392        function parenthesizeConciseBodyOfArrowFunction(body: Expression): Expression;
393        function parenthesizeConciseBodyOfArrowFunction(body: ConciseBody): ConciseBody;
394        function parenthesizeConciseBodyOfArrowFunction(body: ConciseBody): ConciseBody {
395            if (!isBlock(body) && (isCommaSequence(body) || getLeftmostExpression(body, /*stopAtCallExpressions*/ false).kind === SyntaxKind.ObjectLiteralExpression)) {
396                // TODO(rbuckton): Verifiy whether `setTextRange` is needed.
397                return setTextRange(factory.createParenthesizedExpression(body), body);
398            }
399
400            return body;
401        }
402
403        // Type[Extends] :
404        //     FunctionOrConstructorType
405        //     ConditionalType[?Extends]
406
407        // ConditionalType[Extends] :
408        //     UnionType[?Extends]
409        //     [~Extends] UnionType[~Extends] `extends` Type[+Extends] `?` Type[~Extends] `:` Type[~Extends]
410        //
411        // - The check type (the `UnionType`, above) does not allow function, constructor, or conditional types (they must be parenthesized)
412        // - The extends type (the first `Type`, above) does not allow conditional types (they must be parenthesized). Function and constructor types are fine.
413        // - The true and false branch types (the second and third `Type` non-terminals, above) allow any type
414        function parenthesizeCheckTypeOfConditionalType(checkType: TypeNode): TypeNode {
415            switch (checkType.kind) {
416                case SyntaxKind.FunctionType:
417                case SyntaxKind.ConstructorType:
418                case SyntaxKind.ConditionalType:
419                    return factory.createParenthesizedType(checkType);
420            }
421            return checkType;
422        }
423
424        function parenthesizeExtendsTypeOfConditionalType(extendsType: TypeNode): TypeNode {
425            switch (extendsType.kind) {
426                case SyntaxKind.ConditionalType:
427                    return factory.createParenthesizedType(extendsType);
428            }
429            return extendsType;
430        }
431
432        // UnionType[Extends] :
433        //     `|`? IntersectionType[?Extends]
434        //     UnionType[?Extends] `|` IntersectionType[?Extends]
435        //
436        // - A union type constituent has the same precedence as the check type of a conditional type
437        function parenthesizeConstituentTypeOfUnionType(type: TypeNode) {
438            switch (type.kind) {
439                case SyntaxKind.UnionType: // Not strictly necessary, but a union containing a union should have been flattened
440                case SyntaxKind.IntersectionType: // Not strictly necessary, but makes generated output more readable and avoids breaks in DT tests
441                    return factory.createParenthesizedType(type);
442            }
443            return parenthesizeCheckTypeOfConditionalType(type);
444        }
445
446        function parenthesizeConstituentTypesOfUnionType(members: readonly TypeNode[]): NodeArray<TypeNode> {
447            return factory.createNodeArray(sameMap(members, parenthesizeConstituentTypeOfUnionType));
448        }
449
450        // IntersectionType[Extends] :
451        //     `&`? TypeOperator[?Extends]
452        //     IntersectionType[?Extends] `&` TypeOperator[?Extends]
453        //
454        // - An intersection type constituent does not allow function, constructor, conditional, or union types (they must be parenthesized)
455        function parenthesizeConstituentTypeOfIntersectionType(type: TypeNode) {
456            switch (type.kind) {
457                case SyntaxKind.UnionType:
458                case SyntaxKind.IntersectionType: // Not strictly necessary, but an intersection containing an intersection should have been flattened
459                    return factory.createParenthesizedType(type);
460            }
461            return parenthesizeConstituentTypeOfUnionType(type);
462        }
463
464        function parenthesizeConstituentTypesOfIntersectionType(members: readonly TypeNode[]): NodeArray<TypeNode> {
465            return factory.createNodeArray(sameMap(members, parenthesizeConstituentTypeOfIntersectionType));
466        }
467
468        // TypeOperator[Extends] :
469        //     PostfixType
470        //     InferType[?Extends]
471        //     `keyof` TypeOperator[?Extends]
472        //     `unique` TypeOperator[?Extends]
473        //     `readonly` TypeOperator[?Extends]
474        //
475        function parenthesizeOperandOfTypeOperator(type: TypeNode) {
476            switch (type.kind) {
477                case SyntaxKind.IntersectionType:
478                    return factory.createParenthesizedType(type);
479            }
480            return parenthesizeConstituentTypeOfIntersectionType(type);
481        }
482
483        function parenthesizeOperandOfReadonlyTypeOperator(type: TypeNode) {
484            switch (type.kind) {
485                case SyntaxKind.TypeOperator:
486                    return factory.createParenthesizedType(type);
487            }
488            return parenthesizeOperandOfTypeOperator(type);
489        }
490
491        // PostfixType :
492        //     NonArrayType
493        //     NonArrayType [no LineTerminator here] `!` // JSDoc
494        //     NonArrayType [no LineTerminator here] `?` // JSDoc
495        //     IndexedAccessType
496        //     ArrayType
497        //
498        // IndexedAccessType :
499        //     NonArrayType `[` Type[~Extends] `]`
500        //
501        // ArrayType :
502        //     NonArrayType `[` `]`
503        //
504        function parenthesizeNonArrayTypeOfPostfixType(type: TypeNode) {
505            switch (type.kind) {
506                case SyntaxKind.InferType:
507                case SyntaxKind.TypeOperator:
508                case SyntaxKind.TypeQuery: // Not strictly necessary, but makes generated output more readable and avoids breaks in DT tests
509                    return factory.createParenthesizedType(type);
510            }
511            return parenthesizeOperandOfTypeOperator(type);
512        }
513
514        // TupleType :
515        //     `[` Elision? `]`
516        //     `[` NamedTupleElementTypes `]`
517        //     `[` NamedTupleElementTypes `,` Elision? `]`
518        //     `[` TupleElementTypes `]`
519        //     `[` TupleElementTypes `,` Elision? `]`
520        //
521        // NamedTupleElementTypes :
522        //     Elision? NamedTupleMember
523        //     NamedTupleElementTypes `,` Elision? NamedTupleMember
524        //
525        // NamedTupleMember :
526        //     Identifier `?`? `:` Type[~Extends]
527        //     `...` Identifier `:` Type[~Extends]
528        //
529        // TupleElementTypes :
530        //     Elision? TupleElementType
531        //     TupleElementTypes `,` Elision? TupleElementType
532        //
533        // TupleElementType :
534        //     Type[~Extends] // NOTE: Needs cover grammar to disallow JSDoc postfix-optional
535        //     OptionalType
536        //     RestType
537        //
538        // OptionalType :
539        //     Type[~Extends] `?` // NOTE: Needs cover grammar to disallow JSDoc postfix-optional
540        //
541        // RestType :
542        //     `...` Type[~Extends]
543        //
544        function parenthesizeElementTypesOfTupleType(types: readonly (TypeNode | NamedTupleMember)[]): NodeArray<TypeNode> {
545            return factory.createNodeArray(sameMap(types, parenthesizeElementTypeOfTupleType));
546        }
547
548        function parenthesizeElementTypeOfTupleType(type: TypeNode | NamedTupleMember): TypeNode {
549            if (hasJSDocPostfixQuestion(type)) return factory.createParenthesizedType(type);
550            return type;
551        }
552
553        function hasJSDocPostfixQuestion(type: TypeNode | NamedTupleMember): boolean {
554            if (isJSDocNullableType(type)) return type.postfix;
555            if (isNamedTupleMember(type)) return hasJSDocPostfixQuestion(type.type);
556            if (isFunctionTypeNode(type) || isConstructorTypeNode(type) || isTypeOperatorNode(type)) return hasJSDocPostfixQuestion(type.type);
557            if (isConditionalTypeNode(type)) return hasJSDocPostfixQuestion(type.falseType);
558            if (isUnionTypeNode(type)) return hasJSDocPostfixQuestion(last(type.types));
559            if (isIntersectionTypeNode(type)) return hasJSDocPostfixQuestion(last(type.types));
560            if (isInferTypeNode(type)) return !!type.typeParameter.constraint && hasJSDocPostfixQuestion(type.typeParameter.constraint);
561            return false;
562        }
563
564        function parenthesizeTypeOfOptionalType(type: TypeNode): TypeNode {
565            if (hasJSDocPostfixQuestion(type)) return factory.createParenthesizedType(type);
566            return parenthesizeNonArrayTypeOfPostfixType(type);
567        }
568
569        // function parenthesizeMemberOfElementType(member: TypeNode): TypeNode {
570        //     switch (member.kind) {
571        //         case SyntaxKind.UnionType:
572        //         case SyntaxKind.IntersectionType:
573        //         case SyntaxKind.FunctionType:
574        //         case SyntaxKind.ConstructorType:
575        //             return factory.createParenthesizedType(member);
576        //     }
577        //     return parenthesizeMemberOfConditionalType(member);
578        // }
579
580        // function parenthesizeElementTypeOfArrayType(member: TypeNode): TypeNode {
581        //     switch (member.kind) {
582        //         case SyntaxKind.TypeQuery:
583        //         case SyntaxKind.TypeOperator:
584        //         case SyntaxKind.InferType:
585        //             return factory.createParenthesizedType(member);
586        //     }
587        //     return parenthesizeMemberOfElementType(member);
588        // }
589
590        function parenthesizeLeadingTypeArgument(node: TypeNode) {
591            return isFunctionOrConstructorTypeNode(node) && node.typeParameters ? factory.createParenthesizedType(node) : node;
592        }
593
594        function parenthesizeOrdinalTypeArgument(node: TypeNode, i: number) {
595            return i === 0 ? parenthesizeLeadingTypeArgument(node) : node;
596        }
597
598        function parenthesizeTypeArguments(typeArguments: NodeArray<TypeNode> | undefined): NodeArray<TypeNode> | undefined {
599            if (some(typeArguments)) {
600                return factory.createNodeArray(sameMap(typeArguments, parenthesizeOrdinalTypeArgument));
601            }
602        }
603    }
604
605    export const nullParenthesizerRules: ParenthesizerRules = {
606        getParenthesizeLeftSideOfBinaryForOperator: _ => identity,
607        getParenthesizeRightSideOfBinaryForOperator: _ => identity,
608        parenthesizeLeftSideOfBinary: (_binaryOperator, leftSide) => leftSide,
609        parenthesizeRightSideOfBinary: (_binaryOperator, _leftSide, rightSide) => rightSide,
610        parenthesizeExpressionOfComputedPropertyName: identity,
611        parenthesizeConditionOfConditionalExpression: identity,
612        parenthesizeBranchOfConditionalExpression: identity,
613        parenthesizeExpressionOfExportDefault: identity,
614        parenthesizeExpressionOfNew: expression => cast(expression, isLeftHandSideExpression),
615        parenthesizeLeftSideOfAccess: expression => cast(expression, isLeftHandSideExpression),
616        parenthesizeOperandOfPostfixUnary: operand => cast(operand, isLeftHandSideExpression),
617        parenthesizeOperandOfPrefixUnary: operand => cast(operand, isUnaryExpression),
618        parenthesizeExpressionsOfCommaDelimitedList: nodes => cast(nodes, isNodeArray),
619        parenthesizeExpressionForDisallowedComma: identity,
620        parenthesizeExpressionOfExpressionStatement: identity,
621        parenthesizeConciseBodyOfArrowFunction: identity,
622        parenthesizeCheckTypeOfConditionalType: identity,
623        parenthesizeExtendsTypeOfConditionalType: identity,
624        parenthesizeConstituentTypesOfUnionType: nodes => cast(nodes, isNodeArray),
625        parenthesizeConstituentTypeOfUnionType: identity,
626        parenthesizeConstituentTypesOfIntersectionType: nodes => cast(nodes, isNodeArray),
627        parenthesizeConstituentTypeOfIntersectionType: identity,
628        parenthesizeOperandOfTypeOperator: identity,
629        parenthesizeOperandOfReadonlyTypeOperator: identity,
630        parenthesizeNonArrayTypeOfPostfixType: identity,
631        parenthesizeElementTypesOfTupleType: nodes => cast(nodes, isNodeArray),
632        parenthesizeElementTypeOfTupleType: identity,
633        parenthesizeTypeOfOptionalType: identity,
634        parenthesizeTypeArguments: nodes => nodes && cast(nodes, isNodeArray),
635        parenthesizeLeadingTypeArgument: identity,
636    };
637}
638