Skip to Content

Format rules

ttsc is the standalone TypeScript-Go compiler — see Setup if you haven’t installed it yet.

The format/* subset of @ttsc/lint — semicolons, quotes, trailing commas, import ordering, JSDoc normalization, and the Wadler-style print-width reflow. This is what replaces Prettier in a ttsc project.

The format block (canonical)

The recommended way to configure formatting is a Prettier-style format block in lint.config.ts. Keys mirror .prettierrc:

// lint.config.ts import type { TtscLintConfig } from "@ttsc/lint"; export default { format: { severity: "warning", printWidth: 100, singleQuote: true, trailingComma: "all", importOrder: ["<THIRD_PARTY_MODULES>", "@api(.*)$", "^[./]"], jsdoc: true, }, rules: { "no-var": "error", }, } satisfies TtscLintConfig;

Presence of the block (even empty format: {}) enables the always-on format rules at Prettier defaults. Each format key drives one rule:

RuleDriven by format keyEffect
format/semisemiInsert trailing semicolons on ASI-terminated statements.
format/quotessingleQuoteConvert quoted strings to the preferred quote style.
format/trailing-commatrailingCommaAdd trailing commas to multi-line lists.
format/print-widthprintWidth, tabWidth, useTabs, endOfLinePrettier-style line reflow.
format/sort-importsimportOrder (opt-in)Group external/relative imports and alphabetize them.
format/jsdocjsdoc (opt-in)Normalize JSDoc blocks toward prettier-plugin-jsdoc.

Both format/sort-imports and format/jsdoc are opt-in: they only run when you set their format keys. Every other format rule runs as soon as a format block is present.

format.severity

format.severity (default "warning") sets the diagnostic level for every format rule in ttsc check. It is a property of the format block, not an external escape hatch:

format: { severity: "off" }

"off" zeros every format rule and skips ttsc format rewrites — a one-line CI escape hatch during a refactor.

Per-rule overrides

To bump (or relax) one specific rule, drop a sibling rules entry — rules wins on conflict:

export default { format: { semi: true, severity: "warning" }, rules: { "format/semi": "error" }, // promote this one to error } satisfies TtscLintConfig;

The individual rules

format/semi

Enforce trailing semicolons on statements that need them. Default: required. Autofixable. Driven by format.semi.

format/quotes

One quote style throughout. Pick "double" (default) or "single". Backticks always allowed for template literals. Driven by format.singleQuote.

format/trailing-comma

Enforce trailing commas in multiline literals — object literals, array literals, function parameter lists, function call arguments, and import/export specifier lists. Single-line literals are untouched. Driven by format.trailingComma.

format/sort-imports

Opt-in. Reorder and group imports.

Default grouping (each group separated by a blank line):

  1. Built-in Node modules (node:fs, assert, …)
  2. Bare-specifier external modules (react, typia, …)
  3. Internal scoped modules (@/foo, @lib/...)
  4. Relative parent-directory modules (../foo)
  5. Relative same-directory modules (./bar)
  6. CSS / asset imports

Within each group, alphabetical. Autofixable. Set format.importOrder to enable.

format/jsdoc

Opt-in. Normalize JSDoc block spacing and wrapping.

Handles:

  • Single space after * on continuation lines.
  • Tag synonyms (@return@returns, @desc@description, …).
  • Aligns multi-line @param descriptions.

Autofixable. Set format.jsdoc: true (or an options object) to enable.

format/print-width

Column-aware reflow. Mirrors Prettier’s printWidth, tabWidth, useTabs, and endOfLine. The most distinctive rule in the format set — the one that actually reshapes your code.

Driven by format.printWidth, format.tabWidth, format.useTabs, format.endOfLine (defaults: 80, 2, false, "lf").

What it does

The rule looks at each list-shaped node in the file (object literals, array literals, call argument lists, named imports, named exports, …) and renders it in two candidate layouts:

  • Flat — one line, no internal breaks.
  • Broken — opening punctuator on the head line, one entry per indented line, closing punctuator on its own line, trailing comma.

It measures the flat form. If flat fits within printWidth (counting the column where the node starts), the flat layout is kept. If it doesn’t fit, the broken layout is emitted.

// Before (printWidth: 20) const user = { id: "8f5d2f3a", name: "Sam", email: "samchon.github@gmail.com" }; // After const user = { id: "8f5d2f3a", name: "Sam", email: "samchon.github@gmail.com", };

When the rule can’t reflow a node (a shape it doesn’t recognize, or a comment sits inside it), it abstains — no edits applied, original bytes pass through verbatim.

What it reflows

ShapeExample beforeExample after (printWidth: 20)
Object literalconst x = { aaa: 1, bbb: 2, ccc: 3 };multi-line with trailing comma
Array literalconst x = ["alpha", "beta", "gamma"];multi-line with trailing comma
Call expressionprocess(aaaaaa, bbbbbb, cccccc);callee on head line, one arg per indented line
new expressionnew Foo(aaaaaa, bbbbbb, cccccc);same as call, new keyword preserved
Type-argument callfoo<Alpha>(aaaaaa, bbbbbb, cccccc);type arguments stay flat; value arguments break
Optional-callfoo?.(aaaaaa, bbbbbb, cccccc);?. token preserved between callee and (
Named importsimport { alpha, bravo, charlie } from "x";multi-line specifier list
Named exportsexport { alpha, bravo, charlie };multi-line specifier list
import type { … }preserves the type modifiersame

What it does NOT touch (yet)

  • JSX elements and fragments.
  • Conditional, binary, and arrow expressions.
  • Destructuring patterns.
  • Decorators.
  • Multi-line string and template literals.
  • Lists with comments interleaved between members (the rule detects this and abstains).
  • Combined import Default, { … } and import * as ns from "x" declarations.
  • Import-attribute clauses (with {} / assert {} payloads).

For every uncovered shape the rule produces zero findings and zero edits.

Limitations

  • Comments between members of a covered list make the rule abstain. Inline-comment handling is on the roadmap.
  • No “magic comma” hint (Prettier’s preserve-multi-line trick). Width is the only factor in the reflow decision.
  • Long chained call sites like someLong.chained.call(a, b) only reflow the argument list; the callee chain is not split. Chain-aware printing is on the roadmap.
  • The rule never inserts or removes blank lines between top-level statements.

Back-compat: per-rule tuple form

The rule-level tuple form still works and is occasionally useful when you need a one-off override without adding a format block. Treat it as legacy — new projects should use the format block above.

// lint.config.ts import type { TtscLintConfig } from "@ttsc/lint"; export default { rules: { "format/semi": "error", "format/quotes": ["error", { prefer: "double" }], "format/trailing-comma": "error", "format/print-width": ["error", { printWidth: 80 }], }, } satisfies TtscLintConfig;

Rule interactions

format/print-width and format/trailing-comma overlap in one place: the broken layout that print-width emits always has a trailing comma (structurally, not because of the rule). So leaving format/trailing-comma at "off" doesn’t strip trailing commas from reflowed multi-line literals — only from manually-written ones.

ttsc format re-runs every enabled rule until the file is stable, so rule order in lint.config.ts doesn’t affect the final result.

See also

Last updated on