Prettier 3.1.1
Playground link
--parser babel
--print-width 120
Input:
// CASE 1:
// Normal behavior, the `isEqual` call breaks.
function someFunctionName(
someLongBreakingParameterName,
anotherLongParameterName,
) {
return isEqual(a.map(([t, _]) => t?.id), b.map(([t, _]) => t?.id));
}
// When access to `a` and `b` is optional, typescript doesn't break
// the call, but babel and babel-ts do.
function someFunctionName(
someLongBreakingParameterName,
anotherLongParameterName,
) {
return isEqual(a?.map(([t, _]) => t?.id), b?.map(([t, _]) => t?.id));
}
// CASE 2:
// Normal behavior, the `filter` call uses a grouped layout (puts the
// arrow expression parameters on the same line as "filter").
const theValue = Object.entries(someLongObjectName).filter(
([listingId]) => someListToCompareToHere.includes(listingId),
);
// With an optional member expression, babel-ts keeps this as a grouped
// layout, but typescript ungroups it onto a new line.
const theValue = Object.entries(someLongObjectName).filter(
([listingId]) => someListToCompareToHere?.includes(listingId),
);
// CASE 3:
// Normal behavior, the template expression breaks at the first interpolation.
logger.log(
`A long template string with a conditional: ${channel.id}, and then some more content that continues until ${JSON.stringify(location)}`
);
// With an optional member expression, typescript doesn't break the first
// interpolation and instead breaks the second.
logger.log(
`A long template string with a conditional: ${channel?.id}, and then some more content that continues until ${JSON.stringify(location)}`
);
Output:
// CASE 1:
// Normal behavior, the `isEqual` call breaks.
function someFunctionName(someLongBreakingParameterName, anotherLongParameterName) {
return isEqual(
a.map(([t, _]) => t?.id),
b.map(([t, _]) => t?.id),
);
}
// When access to `a` and `b` is optional, typescript doesn't break
// the call, but babel and babel-ts do.
function someFunctionName(someLongBreakingParameterName, anotherLongParameterName) {
return isEqual(
a?.map(([t, _]) => t?.id),
b?.map(([t, _]) => t?.id),
);
}
// CASE 2:
// Normal behavior, the `filter` call uses a grouped layout (puts the
// arrow expression parameters on the same line as "filter").
const theValue = Object.entries(someLongObjectName).filter(([listingId]) =>
someListToCompareToHere.includes(listingId),
);
// With an optional member expression, babel-ts keeps this as a grouped
// layout, but typescript ungroups it onto a new line.
const theValue = Object.entries(someLongObjectName).filter(([listingId]) =>
someListToCompareToHere?.includes(listingId),
);
// CASE 3:
// Normal behavior, the template expression breaks at the first interpolation.
logger.log(
`A long template string with a conditional: ${
channel.id
}, and then some more content that continues until ${JSON.stringify(location)}`,
);
// With an optional member expression, typescript doesn't break the first
// interpolation and instead breaks the second.
logger.log(
`A long template string with a conditional: ${
channel?.id
}, and then some more content that continues until ${JSON.stringify(location)}`,
);
Expected behavior:
The output from each of these 3 cases should match regardless of which parser is used (babel, babel-ts, or typescript).
The consistent difference here is whether the expression is optional or not. Inspecting the parsed ASTs, Babel (and thus babel-ts) parses these as distinct node types (OptionalCallExpression and OptionalMemberExpression), but the TypeScript parser just uses the plain CallExpression and MemberExpression nodes with an extra optional property on them.
From #10244, it seems like maybe in the past (or in a different situation), these were parsed as ChainExpression, which then got transformed by Prettier into the appropriate optional expression nodes, but that is no longer the case, so the transform doesn't pick them up and convert as needed.
Prettier 3.1.1
Playground link
Input:
Output:
Expected behavior:
The output from each of these 3 cases should match regardless of which parser is used (
babel,babel-ts, ortypescript).The consistent difference here is whether the expression is
optionalor not. Inspecting the parsed ASTs, Babel (and thus babel-ts) parses these as distinct node types (OptionalCallExpressionandOptionalMemberExpression), but the TypeScript parser just uses the plainCallExpressionandMemberExpressionnodes with an extraoptionalproperty on them.From #10244, it seems like maybe in the past (or in a different situation), these were parsed as
ChainExpression, which then got transformed by Prettier into the appropriate optional expression nodes, but that is no longer the case, so the transform doesn't pick them up and convert as needed.