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