Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions crates/oxc_formatter/src/parentheses/ts_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,20 @@ impl NeedsParentheses<'_> for AstNode<'_, TSType<'_>> {
impl NeedsParentheses<'_> for AstNode<'_, TSFunctionType<'_>> {
#[inline]
fn needs_parentheses(&self, _f: &Formatter<'_, '_>) -> bool {
function_like_type_needs_parentheses(
self.span(),
effective_parent(self.parent()),
Some(&self.return_type),
)
let parent = effective_parent(self.parent());

// TSFunctionType needs parens when used as an arrow function's return type
// to resolve syntactic ambiguity: `(): () => void => ...` is ambiguous without parens.
// TSConstructorType does NOT need this — the `new` keyword is unambiguous.
//
// https://github.com/prettier/prettier/blob/812a4d0071270f61a7aa549d625b618be7e09d71/src/language-js/parentheses/needs-parentheses.js#L514-L525
if let AstNodes::TSTypeAnnotation(type_annotation) = parent
&& matches!(type_annotation.parent(), AstNodes::ArrowFunctionExpression(_))
{
return true;
}

function_like_type_needs_parentheses(self.span(), parent, Some(&self.return_type))
}
}

Expand Down Expand Up @@ -90,9 +99,12 @@ impl NeedsParentheses<'_> for AstNode<'_, TSUnionType<'_>> {
}
}

/// Returns `true` if a TS primary type needs parentheses
/// Common logic for determining if function-like types (TSFunctionType, TSConstructorType)
/// need parentheses based on their parent context.
/// Returns `true` if a function-like TS type (TSFunctionType, TSConstructorType)
/// needs parentheses based on their parent context.
///
/// Note: The arrow-function-return-type case is handled separately by each caller,
/// because only `TSFunctionType` needs parens there (syntactic ambiguity with `=>`),
/// while `TSConstructorType` does not (the `new` keyword is unambiguous).
///
/// Ported from Biome's function_like_type_needs_parentheses
fn function_like_type_needs_parentheses<'a>(
Expand All @@ -101,10 +113,6 @@ fn function_like_type_needs_parentheses<'a>(
return_type: Option<&'a TSTypeAnnotation<'a>>,
) -> bool {
match parent {
// Arrow function return types need parens
AstNodes::TSTypeAnnotation(type_annotation) => {
matches!(type_annotation.parent(), AstNodes::ArrowFunctionExpression(_))
}
// In conditional types
AstNodes::TSConditionalType(conditional) => {
let is_check_type = conditional.check_type().span() == span;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const f = (): (new () => Foo) => new Foo();

const g = (): new () => Foo => new Foo();

export const provideCloudMessagingServiceClass = (): (new () => CloudMessagingService) => {
return MobileCloudMessagingService;
};

const h = (): (new <T>() => T) => new Foo();

const i = (): (abstract new () => Foo) => new Foo();

const j = (): (() => Foo) => () => new Foo();

type T = (new () => Foo) | Bar;
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
source: crates/oxc_formatter/tests/fixtures/mod.rs
assertion_line: 229
---
==================== Input ====================
const f = (): (new () => Foo) => new Foo();

const g = (): new () => Foo => new Foo();

export const provideCloudMessagingServiceClass = (): (new () => CloudMessagingService) => {
return MobileCloudMessagingService;
};

const h = (): (new <T>() => T) => new Foo();

const i = (): (abstract new () => Foo) => new Foo();

const j = (): (() => Foo) => () => new Foo();

type T = (new () => Foo) | Bar;

==================== Output ====================
------------------
{ printWidth: 80 }
------------------
const f = (): new () => Foo => new Foo();

const g = (): new () => Foo => new Foo();

export const provideCloudMessagingServiceClass =
(): new () => CloudMessagingService => {
return MobileCloudMessagingService;
};

const h = (): new <T>() => T => new Foo();

const i = (): abstract new () => Foo => new Foo();

const j = (): (() => Foo) => () => new Foo();

type T = (new () => Foo) | Bar;

-------------------
{ printWidth: 100 }
-------------------
const f = (): new () => Foo => new Foo();

const g = (): new () => Foo => new Foo();

export const provideCloudMessagingServiceClass = (): new () => CloudMessagingService => {
return MobileCloudMessagingService;
};

const h = (): new <T>() => T => new Foo();

const i = (): abstract new () => Foo => new Foo();

const j = (): (() => Foo) => () => new Foo();

type T = (new () => Foo) | Bar;

===================== End =====================
Loading