Skip to content

Keep an own-line comment before a satisfies or as type on its own line - #19939

Merged
fisker merged 14 commits into
prettier:mainfrom
Kjubikstronk:callee-comments
Aug 30, 2026
Merged

fisker merged 14 commits into
prettier:mainfrom
Kjubikstronk:callee-comments

Conversation

@Kjubikstronk

Copy link
Copy Markdown
Contributor

Description

typescript/satisfies-operators/comments-unstable.ts is listed in failed-format-tests.js as unstable. This fixes it and removes the entry.

// Input
const t1 = {
    prop1: 1,
    prop2: 2,
    prop3: 3
} satisfies
// Comment
Record<string, number>;

// Prettier stable (first format)
} satisfies // Comment
Record<string, number>;

// Prettier stable (second format)
} satisfies Record<string, number>; // Comment

// Prettier main
} satisfies
  // Comment
  Record<string, number>;

The operator was followed by a hard " ", so a comment written on its own line before the type was pulled up onto the satisfies line. That turns it into a trailing comment, and the next format moves it to the end of the statement, so the file needed three passes to settle.

Keeping the comment on the line it was written on fixes the instability and leaves it where it was written, rather than at the end of the statement.

Unions are excluded: they already print their members on separate lines, so they do not need the break and would otherwise be indented twice. typescript/union/consistent-with-flow/single-type.ts is unaffected by this change; it is unstable for a different reason (comments on union members) and stays in the list.

Checklist

  • I’ve added tests to confirm my change works.
  • (If changing the API or CLI) I’ve documented the changes I’ve made (in the docs/ directory).
  • (If the change is user-facing) I’ve added my changes to changelog_unreleased/*/XXXX.md file following changelog_unreleased/TEMPLATE.md.
  • I’ve read the contributing guidelines.
  • I did not use AI to generate this PR.
  • (If the above is not checked) I have reviewed the AI-generated content before submitting.

No new test is added because the fixture already exists; it was listed as unstable rather than passing, and that entry is now removed. Verified with the full tests/format/js, typescript, jsx and flow suites: the set of failing tests is identical to main on my machine. I used AI assistance and reviewed everything before submitting.

`x satisfies\n// comment\nT` printed the comment on the `satisfies` line, which
made it a trailing comment. The next format then moved it to the end of the
statement, so the file took three passes to settle.

The comment now keeps the line it was written on. Unions are left alone because
they already break their members onto separate lines and would be indented twice.
@pkg-pr-new

pkg-pr-new Bot commented Aug 28, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

yarn add https://pkg.pr.new/@prettier/[email protected]
yarn add https://pkg.pr.new/@prettier/[email protected]
yarn add https://pkg.pr.new/@prettier/[email protected]
yarn add https://pkg.pr.new/[email protected]

commit: e35ffb3

@netlify

netlify Bot commented Aug 28, 2026

Copy link
Copy Markdown

✅ Deploy Preview for prettier ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 1a86d51
🔍 Latest deploy log https://app.netlify.com/projects/prettier/deploys/6a91b5b54db6580008a83c67
😎 Deploy Preview https://deploy-preview-19939--prettier.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Aug 28, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for prettier ready!

Name Link
🔨 Latest commit e35ffb3
🔍 Latest deploy log https://app.netlify.com/projects/prettier/deploys/6a94b1ae434445000863127c
😎 Deploy Preview https://deploy-preview-19939--prettier.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@Kjubikstronk

Copy link
Copy Markdown
Contributor Author

All four done.

Using isUnionType now.

isFlowAsConstExpression isn't needed. AsConstExpression has no typeAnnotation at all (its keys are type, loc, expression, range), and hasComment short-circuits on node?.comments, so the guard could never change the result. Dropped it, and the flow as-const and as-satisfies-expression suites still pass.

On the fixture: its case was t1 from comments.ts, split out when it became unstable. I folded it back into comments.ts and deleted comments-unstable.ts, so the coverage stays. Snapshot updated.

Changelog trimmed.

node.typeAnnotation,
CommentCheckFlags.Leading | CommentCheckFlags.Line,
(comment) => hasNewline(options.originalText, locEnd(comment)),
);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the same as hasLeadingOwnLineComment?

Please answer directly under this comment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost, but not quite. The difference is CommentCheckFlags.Line.

hasLeadingOwnLineComment matches any leading comment whose end is followed by a newline, so it also catches a block comment that opens on the operator's line and closes on a later one. I tried swapping it in:

1 as /*
  ...
*/
Foo;

currently prints as 1 as /* with Foo; back at column 0, and with hasLeadingOwnLineComment it becomes

1 as
  /*
  ...
  */
  Foo;

which fails tests/format/typescript/as/comments. Inline block comments like 1 as /* c */ Foo are unaffected either way, since there is no newline after them.

So it is a behaviour change rather than a refactor. If you would rather multiline block comments broke that way too, I am happy to drop the flag and update those snapshots, it does simplify this to a single existing helper. I kept it narrow because the change was about line comments.

@fisker fisker Aug 30, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change to

1 as
  /*
  ...
  */
  Foo;

is expected. And I think it will fix the alignment issue cases that the comment is "indentable".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, switched to hasLeadingOwnLineComment and updated the as/comments snapshot to the shape you quoted.

That drops the local hasComment call and the hasNewline and locEnd imports with it, so this file now uses the existing helper rather than its own variant.

Checked the four shapes are stable across two passes: the multiline block comment, a line comment on its own line, an inline 1 as /* c */ Foo, and a union with a comment. The inline one is untouched, since there is no newline after it. Rest of the suite is unchanged against this branch, same 17 pre-existing failures before and after.

The union check is still here rather than in shouldIndentUnionType, per the other thread.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a test for "indentable" comment, can be a jsdoc.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also add a changelog for it. We can add another changelog entry at ‎changelog_unreleased/typescript/19939-2.md‎

// then moves it to the end of the statement. A union already prints its members on
// their own lines, so it does not need the break and would be indented twice.
const keepTypeOnOwnLine =
!isUnionType(node.typeAnnotation) &&

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should include union type here, but exclude in shouldIndentUnionType?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried that and it regresses the long-line cases.

Adding (key === "typeAnnotation" && isBinaryCastExpression(parent)) to shouldIndentUnionType and dropping the union check here breaks as/break-after-keyword:

-const firstItem2 = ______________.items.find((item) => item.type === "item") as
-  string | number;
+const firstItem2 = ______________.items.find(
+  (item) => item.type === "item",
+) as string | number;

and worse on the one that already has a comment:

) as | a_union_will_break // comments

Three suites fail that way, as, as/break-after-keyword and flow/as-satisfies-expression. The indent after as is doing real work for line breaking, so removing it there is not free.

Unless you meant excluding it only when the type carries an own-line comment, which would keep the breaking behaviour and still move the knowledge into shouldIndentUnionType. I can try that shape if it is closer to what you had in mind.

Covers a block comment that opens on the operator's line and closes on a later
one, which the line-comment-only check missed.
// A comment written on its own line before the type has to stay there. Printing it
// after the operator makes it a trailing comment, and the next format then moves it
// to the end of the statement. A union already prints its members on their own
// lines, so it does not need the break and would be indented twice.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is meaningless, let's dro.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All three done.

Comment dropped. The jsdoc case is in as/comments, and it does show the alignment you predicted:

1 as
  /**
   * jsdoc
   */
  Foo;

against stable, which leaves it hanging off the operator:

1 as /**
 * jsdoc
 */
Foo;

Changelog added at changelog_unreleased/typescript/19939-2.md with that pair as the before and after.

@fisker fisker Aug 30, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stop saying "All three done.", just reply comments one by one. There is no context for "three".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood, one reply per thread from here.

@@ -0,0 +1,24 @@
#### Keep a block comment before a `satisfies` or `as` type on its own line (#19939 by @Kjubikstronk)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Point out it's misaligned before in the title.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Retitled to "Fix misaligned block comment before a satisfies or as type", so the heading says what was wrong rather than what it does now.

Comment on lines +6 to +10
const t1 = {
prop1: 1,
} satisfies
// Comment
Record<string, number>;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use

const foo = {
  bar: 1,
} as
  // Comment
  Record<string, number>;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Swapped in, 9626796.

That shape shows the instability better than mine did. Stable prints } as // Comment on the first pass, then moves the comment to the end of the line on the second, so the changelog now has both stable outputs. I ran them rather than writing them out.

@fisker
fisker merged commit 5fd5b69 into prettier:main Aug 30, 2026
35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants