Fix missing parentheses in ConditionalTypeAnnotation (#17196)

This commit is contained in:
fisker Cheung authored and GitHub committed 2025-03-03 09:09:29 +08:00
1 parent 5cdf54dc5f
commit 43d4f962f0
9 files changed
+83 -38

No files matched your search

+16
View File
@@ -0,0 +1,16 @@
#### Fix missing parentheses in `ConditionalTypeAnnotation` (#17196 by @fisker)
<!-- prettier-ignore -->
```jsx
// Input
type T<U> = 'a' | ('b' extends U ? 'c' : empty);
type T<U> = 'a' & ('b' extends U ? 'c' : empty);
// Prettier stable
type T<U> = "a" | "b" extends U ? "c" : empty;
type T<U> = "a" & "b" extends U ? "c" : empty;
// Prettier main
type T<U> = "a" | ("b" extends U ? "c" : empty);
type T<U> = "a" & ("b" extends U ? "c" : empty);
```
+4 -6
View File
@@ -15,6 +15,7 @@ import {
getFunctionParameters,
isCallExpression,
isCallLikeExpression,
isConditionalType,
isIntersectionType,
isMemberExpression,
isObjectProperty,
@@ -365,8 +366,7 @@ function handleNestedConditionalExpressionComments({
const enclosingIsCond =
enclosingNode?.type === "ConditionalExpression" ||
enclosingNode?.type === "ConditionalTypeAnnotation" ||
enclosingNode?.type === "TSConditionalType";
isConditionalType(enclosingNode);
if (!enclosingIsCond) {
return false;
@@ -374,8 +374,7 @@ function handleNestedConditionalExpressionComments({
const followingIsCond =
followingNode?.type === "ConditionalExpression" ||
followingNode?.type === "ConditionalTypeAnnotation" ||
followingNode?.type === "TSConditionalType";
isConditionalType(followingNode);
if (followingIsCond) {
addDanglingComment(enclosingNode, comment);
@@ -399,8 +398,7 @@ function handleConditionalExpressionComments({
if (
(!precedingNode || !isSameLineAsPrecedingNode) &&
(enclosingNode?.type === "ConditionalExpression" ||
enclosingNode?.type === "ConditionalTypeAnnotation" ||
enclosingNode?.type === "TSConditionalType") &&
isConditionalType(enclosingNode)) &&
followingNode
) {
if (
+17 -22
View File
@@ -10,10 +10,13 @@ import {
isBinaryCastExpression,
isBitwiseOperator,
isCallExpression,
isConditionalType,
isIntersectionType,
isMemberExpression,
isNullishCoalescing,
isObjectOrRecordExpression,
isObjectProperty,
isUnionType,
shouldFlatten,
startsWithNoLookaheadToken,
} from "./utils/index.js";
@@ -525,11 +528,20 @@ function needsParens(path, options) {
// fallthrough
case "TSConditionalType":
case "TSConstructorType":
if (key === "extendsType" && parent.type === "TSConditionalType") {
if (node.type === "TSConditionalType") {
return true;
}
case "ConditionalTypeAnnotation":
if (
key === "extendsType" &&
isConditionalType(node) &&
parent.type === node.type
) {
return true;
}
if (key === "checkType" && isConditionalType(parent)) {
return true;
}
if (key === "extendsType" && parent.type === "TSConditionalType") {
let { typeAnnotation } = node.returnType || node.typeAnnotation;
if (
@@ -547,15 +559,11 @@ function needsParens(path, options) {
}
}
if (key === "checkType" && parent.type === "TSConditionalType") {
return true;
}
// fallthrough
case "TSUnionType":
case "TSIntersectionType":
if (
(parent.type === "TSUnionType" ||
parent.type === "TSIntersectionType") &&
(isUnionType(parent) || isIntersectionType(parent)) &&
parent.types.length > 1 &&
(!node.types || node.types.length > 1)
) {
@@ -706,19 +714,6 @@ function needsParens(path, options) {
);
}
case "ConditionalTypeAnnotation":
if (
key === "extendsType" &&
parent.type === "ConditionalTypeAnnotation" &&
node.type === "ConditionalTypeAnnotation"
) {
return true;
}
if (key === "checkType" && parent.type === "ConditionalTypeAnnotation") {
return true;
}
// fallthrough
case "OptionalIndexedAccessType":
return key === "objectType" && parent.type === "IndexedAccessType";
+3 -5
View File
@@ -18,6 +18,7 @@ import {
hasComment,
isBinaryCastExpression,
isCallExpression,
isConditionalType,
isJsxElement,
isLoneShortArgument,
isMemberExpression,
@@ -153,9 +154,7 @@ function printTernary(path, options, print, args) {
const { node } = path;
const isConditionalExpression = node.type === "ConditionalExpression";
const isTSConditional =
node.type === "TSConditionalType" ||
node.type === "ConditionalTypeAnnotation"; // For Flow.
const isTSConditional = isConditionalType(node);
const consequentNodePropertyName = isConditionalExpression
? "consequent"
: "trueType";
@@ -322,8 +321,7 @@ function printTernary(path, options, print, args) {
" ",
"extends",
" ",
node.extendsType.type === "TSConditionalType" ||
node.extendsType.type === "ConditionalTypeAnnotation" ||
isConditionalType(node.extendsType) ||
node.extendsType.type === "TSMappedType"
? print("extendsType")
: group(wrapInParens(print("extendsType"))),
+2 -3
View File
@@ -15,6 +15,7 @@ import {
createTypeCheckFunction,
hasComment,
hasLeadingOwnLineComment,
isConditionalType,
isFlowObjectTypePropertyAFunction,
isObjectType,
isSimpleType,
@@ -174,9 +175,7 @@ function printUnionType(path, options, print) {
// If there's a leading comment, the parent is doing the indentation
const shouldIndent =
parent.type !== "TypeParameterInstantiation" &&
(parent.type !== "TSConditionalType" || !options.experimentalTernaries) &&
(parent.type !== "ConditionalTypeAnnotation" ||
!options.experimentalTernaries) &&
(!isConditionalType(parent) || !options.experimentalTernaries) &&
parent.type !== "TSTypeParameterInstantiation" &&
parent.type !== "GenericTypeAnnotation" &&
parent.type !== "TSTypeReference" &&
+8 -2
View File
@@ -1060,13 +1060,18 @@ const isBinaryCastExpression = createTypeCheckFunction([
]);
const isUnionType = createTypeCheckFunction([
"UnionTypeAnnotation",
"TSUnionType",
"UnionTypeAnnotation",
]);
const isIntersectionType = createTypeCheckFunction([
"IntersectionTypeAnnotation",
"TSIntersectionType",
"IntersectionTypeAnnotation",
]);
const isConditionalType = createTypeCheckFunction([
"TSConditionalType",
"ConditionalTypeAnnotation",
]);
export {
@@ -1092,6 +1097,7 @@ export {
isBitwiseOperator,
isCallExpression,
isCallLikeExpression,
isConditionalType,
isExportDeclaration,
isFlowObjectTypePropertyAFunction,
isFunctionCompositionArgs,
@@ -0,0 +1,26 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`union.js [babel-flow] format 1`] = `
"Unexpected token, expected ")" (1:24)
> 1 | type T<U> = 'a' | ('b' extends U ? 'c' : empty);
| ^
2 | type T<U> = 'a' & ('b' extends U ? 'c' : empty);
3 |
Cause: Unexpected token, expected ")" (1:23)"
`;
exports[`union.js format 1`] = `
====================================options=====================================
parsers: ["flow", "typescript"]
printWidth: 80
| printWidth
=====================================input======================================
type T<U> = 'a' | ('b' extends U ? 'c' : empty);
type T<U> = 'a' & ('b' extends U ? 'c' : empty);
=====================================output=====================================
type T<U> = "a" | ("b" extends U ? "c" : empty);
type T<U> = "a" & ("b" extends U ? "c" : empty);
================================================================================
`;
@@ -0,0 +1,5 @@
runFormatTest(import.meta, ["flow", "typescript"], {
errors: {
"babel-flow": true,
},
});
@@ -0,0 +1,2 @@
type T<U> = 'a' | ('b' extends U ? 'c' : empty);
type T<U> = 'a' & ('b' extends U ? 'c' : empty);