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