Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Superfluous type operation parentheses are not stripped #13500

Open
Gregoor opened this issue Sep 21, 2022 · 5 comments
Open

Superfluous type operation parentheses are not stripped #13500

Gregoor opened this issue Sep 21, 2022 · 5 comments
Labels
area:union types lang:typescript Issues affecting TypeScript-specific constructs (not general JS issues) status:needs discussion Issues needing discussion and a decision to be made before action can be taken

Comments

@Gregoor
Copy link

Gregoor commented Sep 21, 2022

I expected type operation parens stripping would work like the one for regular JS operations, i.e. superfluous parens would be removed. That seems to not be the case. Now I am wondering: is that by design?

Prettier 2.7.1
Playground link

--parser babel

Input:

type T = (1 | 1) | 2;

Output:

type T = (1 | 1) | 2;

Expected behavior:

type T = 1 | 1 | 2;
@thorn0 thorn0 added status:needs discussion Issues needing discussion and a decision to be made before action can be taken lang:typescript Issues affecting TypeScript-specific constructs (not general JS issues) area:union types labels Sep 22, 2022
@thorn0
Copy link
Member

thorn0 commented Sep 22, 2022

It is by design because usually Prettier tries to preserve the shape of the AST. There are some exceptions from this rule. E.g., a || (b || c) is printed as a || b || c, which has a different representation in the AST. We may add similar logic for union and intersection types, but would such a change have real practical value? This case looks like something that rarely pops up in practice.

@Gregoor
Copy link
Author

Gregoor commented Sep 23, 2022

That makes sense, I was suspecting I'd be in a niche case. Some context for my case: I am building a tool that helps with code transformations and it parenthesizes operations when you insert them, to make sure new operations get the highest precedence, while superfluous parens would be stripped (which is what I assumed, based on the JS expression behavior, as shown by you). Totally understand if that's outside of scope for Prettier though!

@thorn0
Copy link
Member

thorn0 commented Sep 23, 2022

Your tool can flatten unions and intersection itself, but let's keep the issue open for discussion. There might be other arguments for or against doing this in Prettier.

@Gregoor Gregoor mentioned this issue Dec 13, 2022
2 tasks
@bradzacher
Copy link

bradzacher commented Apr 18, 2023

I have the same issue right now (typescript-eslint/typescript-eslint#6899) - generating some code using strings and wanting prettier to automatically unwrap unnecessarily parenthesised unions.

From a human-written code POV - I can see this from an code formatting & standardisation POV - it's easy to leave unnecessary parens behind after refactoring like

type T = (A | B) & C;
// refactor | to &
type T = (A & B) & C;
// oops parens don't get removed automatically

In these super simple examples it's really easy to see the unnecessary parentheses and remove them by hand - but if you've got a union/intersection of object literals or generic types - things can get pretty messy and it can be hard to manually untangle it all.


Prettier does currently also enforce parenthesisation of "ambiguous" type expressions (that are equivalent due to operator precedence):

type T = {a: string} | {b: string} & {c: string} | {d: string}
// prettier formats as
type T = {a: string} | ({b: string} & {c: string}) | {d: string};

Though that doesn't change the AST at all.

@bradzacher
Copy link

bradzacher commented Apr 18, 2023

Here is an example of why it would be great for prettier to remove the unnecessary parentheses:

// with unnecessary parentheses :(
type T = ({
  a: string } | { b: string }) | { c: string }

// without unnecessary parentheses :)
type T = {
  a: string } | { b: string } | { c: string }

formatted:

// with unnecessary parentheses :(
type T =
  | (
      | {
          a: string;
        }
      | { b: string }
    )
  | { c: string };

// without unnecessary parentheses :)
type T =
  | {
      a: string;
    }
  | { b: string }
  | { c: string };

playground

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:union types lang:typescript Issues affecting TypeScript-specific constructs (not general JS issues) status:needs discussion Issues needing discussion and a decision to be made before action can be taken
Projects
None yet
Development

No branches or pull requests

3 participants