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:
| Rule | Driven by format key | Effect |
|---|---|---|
format/semi | semi | Insert trailing semicolons on ASI-terminated statements. |
format/quotes | singleQuote | Convert quoted strings to the preferred quote style. |
format/trailing-comma | trailingComma | Add trailing commas to multi-line lists. |
format/print-width | printWidth, tabWidth, useTabs, endOfLine | Prettier-style line reflow. |
format/sort-imports | importOrder (opt-in) | Group external/relative imports and alphabetize them. |
format/jsdoc | jsdoc (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):
- Built-in Node modules (
node:fs,assert, …) - Bare-specifier external modules (
react,typia, …) - Internal scoped modules (
@/foo,@lib/...) - Relative parent-directory modules (
../foo) - Relative same-directory modules (
./bar) - 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
@paramdescriptions.
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
| Shape | Example before | Example after (printWidth: 20) |
|---|---|---|
| Object literal | const x = { aaa: 1, bbb: 2, ccc: 3 }; | multi-line with trailing comma |
| Array literal | const x = ["alpha", "beta", "gamma"]; | multi-line with trailing comma |
| Call expression | process(aaaaaa, bbbbbb, cccccc); | callee on head line, one arg per indented line |
new expression | new Foo(aaaaaa, bbbbbb, cccccc); | same as call, new keyword preserved |
| Type-argument call | foo<Alpha>(aaaaaa, bbbbbb, cccccc); | type arguments stay flat; value arguments break |
| Optional-call | foo?.(aaaaaa, bbbbbb, cccccc); | ?. token preserved between callee and ( |
| Named imports | import { alpha, bravo, charlie } from "x"; | multi-line specifier list |
| Named exports | export { alpha, bravo, charlie }; | multi-line specifier list |
import type { … } | preserves the type modifier | same |
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, { … }andimport * 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
ttsc fix&ttsc format— applying format autofixes.- Rules — the full lint-rule catalog.