From c8c5341d61f16e7360284bcd63073a8169a2b47f Mon Sep 17 00:00:00 2001 From: leaysgur <6259812+leaysgur@users.noreply.github.com> Date: Fri, 26 Dec 2025 03:38:42 +0000 Subject: [PATCH] fix(formatter): Complete `is_complex_type_arguments()` (take2) (#17362) Fixes #17275, without `memoized.inspect(f)`. --- .../src/utils/assignment_like.rs | 49 ++++++++++++------- .../ts/assignments/complex-type-arguments.ts | 11 +++++ .../complex-type-arguments.ts.snap | 33 +++++++++++++ 3 files changed, 74 insertions(+), 19 deletions(-) diff --git a/crates/oxc_formatter/src/utils/assignment_like.rs b/crates/oxc_formatter/src/utils/assignment_like.rs index 09a231f8af6c7..d3275087264c0 100644 --- a/crates/oxc_formatter/src/utils/assignment_like.rs +++ b/crates/oxc_formatter/src/utils/assignment_like.rs @@ -989,38 +989,49 @@ fn is_short_argument(argument: &Expression, threshold: u16, f: &Formatter) -> bo } } -/// This function checks if `TSTypeArguments` is complex -/// We need it to decide if `CallExpression` with the type arguments is breakable or not -/// If the type arguments is complex the function call is breakable +/// This function checks if `TSTypeArguments` is complex. +/// We need it to decide if `CallExpression` with the type arguments is breakable or not. +/// If the type arguments is complex the function call is breakable. +/// +/// NOTE: This function does not follow Prettier exactly. /// [Prettier applies]: fn is_complex_type_arguments(type_arguments: &TSTypeParameterInstantiation) -> bool { - let ts_type_argument_list = &type_arguments.params; - - if ts_type_argument_list.len() > 1 { - return true; - } - - let is_first_argument_complex = ts_type_argument_list.first().is_some_and(|first_argument| { + let is_complex_ts_type = |ts_type: &TSType| { matches!( - first_argument, + ts_type, TSType::TSUnionType(_) | TSType::TSIntersectionType(_) | TSType::TSTypeLiteral(_) - // TODO: This is not part of Prettier's logic, but it makes sense to consider mapped types as - // complex because it is the same as type literals in terms of structure. - // NOTE: Once the `will_break` logic is added, this will have to be revisited. + // NOTE: Prettier does not contain `TSMappedType` in its check. + // But it makes sense to consider mapped types as complex, + // because it is the same as type literals in terms of structure. | TSType::TSMappedType(_) ) - }); + }; - if is_first_argument_complex { + if type_arguments.params.len() > 1 { return true; } - // TODO: add here will_break logic - // https://github.com/prettier/prettier/blob/a043ac0d733c4d53f980aa73807a63fc914f23bd/src/language-js/print/assignment.js#L454 + type_arguments.params.first().is_some_and(|first_argument| { + if is_complex_ts_type(first_argument) { + return true; + } + + // NOTE: Prettier checks `willBreak(print(typeArgs))` here. + // Our equivalent is `type_arguments.memoized().inspect(f).will_break()`, + // but we avoid using it because: + // - `inspect(f)` (= `f.intern()`) will update the comment counting state in `f` + // - And resulted IRs are discarded after this check + // So we approximate it by checking if the type arguments contain complex types. + if let TSType::TSTypeReference(type_ref) = first_argument + && let Some(type_args) = &type_ref.type_arguments + { + return is_complex_type_arguments(type_args); + } - false + false + }) } /// [Prettier applies]: diff --git a/crates/oxc_formatter/tests/fixtures/ts/assignments/complex-type-arguments.ts b/crates/oxc_formatter/tests/fixtures/ts/assignments/complex-type-arguments.ts index 6273db8985a3d..d4650dcc833b5 100644 --- a/crates/oxc_formatter/tests/fixtures/ts/assignments/complex-type-arguments.ts +++ b/crates/oxc_formatter/tests/fixtures/ts/assignments/complex-type-arguments.ts @@ -7,3 +7,14 @@ const emitter = createGlobalEmitter<{ const emitter2 = createGlobalEmitter<{ longlonglonglongKey: Extract }>() + +// Type argument is an `TSTypeReference` +// https://github.com/oxc-project/oxc/issues/17275 +export class Test { + readonly coordinates = model.required< + Immutable<{ + latitude: number; + longitude: number; + }> + >(); +} diff --git a/crates/oxc_formatter/tests/fixtures/ts/assignments/complex-type-arguments.ts.snap b/crates/oxc_formatter/tests/fixtures/ts/assignments/complex-type-arguments.ts.snap index 0e1a05c7a89c5..5207583918ff9 100644 --- a/crates/oxc_formatter/tests/fixtures/ts/assignments/complex-type-arguments.ts.snap +++ b/crates/oxc_formatter/tests/fixtures/ts/assignments/complex-type-arguments.ts.snap @@ -12,6 +12,17 @@ const emitter2 = createGlobalEmitter<{ longlonglonglongKey: Extract }>() +// Type argument is an `TSTypeReference` +// https://github.com/oxc-project/oxc/issues/17275 +export class Test { + readonly coordinates = model.required< + Immutable<{ + latitude: number; + longitude: number; + }> + >(); +} + ==================== Output ==================== ------------------ { printWidth: 80 } @@ -26,6 +37,17 @@ const emitter2 = createGlobalEmitter<{ longlonglonglongKey: Extract; }>(); +// Type argument is an `TSTypeReference` +// https://github.com/oxc-project/oxc/issues/17275 +export class Test { + readonly coordinates = model.required< + Immutable<{ + latitude: number; + longitude: number; + }> + >(); +} + ------------------- { printWidth: 100 } ------------------- @@ -39,4 +61,15 @@ const emitter2 = createGlobalEmitter<{ longlonglonglongKey: Extract; }>(); +// Type argument is an `TSTypeReference` +// https://github.com/oxc-project/oxc/issues/17275 +export class Test { + readonly coordinates = model.required< + Immutable<{ + latitude: number; + longitude: number; + }> + >(); +} + ===================== End =====================