TypeScript¶
Files ending in .ts, .mts, .cts and .tsx are formatted by the
TypeScript pack (rainbow_fmt.languages.typescript, grammars
tree-sitter-typescript
typescript and tsx). It is built on the JavaScript pack: everything in
javascript.md applies, and this page describes what
types add. The defaults follow Prettier's rules, with the core's 4-space
indentation and without semicolons.
interface User{id:number,name:string;email?:string}
type Status="active"|"suspended"|"deleted"|"pending_verification"|"archived"
export async function load<T extends User>(id:number,cache:Map<number,T>):Promise<T|undefined>{return cache.get(id)}
becomes
interface User {
id: number
name: string
email?: string
}
type Status =
| "active"
| "suspended"
| "deleted"
| "pending_verification"
| "archived"
export async function load<T extends User>(
id: number,
cache: Map<number, T>,
): Promise<T | undefined> {
return cache.get(id)
}
Declaration files¶
Declaration files (*.d.ts, *.d.mts, *.d.cts) are often generated, so
they are skipped when a directory is formatted
(rainbow-fmt format src). A declaration file named on the command line
is formatted (rainbow-fmt format src/types.d.ts).
What changes and what does not¶
As in JavaScript, the pack changes layout only. Besides JavaScript's
optional tokens, it may add or remove the separators between the members
of an interface or type literal, trailing commas in type parameters,
tuples and enums, and the leading | or & of a union or intersection;
the verifier checks that nothing else changed.
- Type annotations:
x: T,f(): T,x?: T,x!: T; no space before the:,?or!, one after the:. - Interfaces are always broken, one member per line. Type literals
(
{ a: A; b: B }) wrap like objects: flat if they fit, broken when too long or written with a line break after{(object_wrap). - Member separators of interfaces and type literals follow
semicolons(see below); commas become semicolons. - Unions that do not fit put each member on its own line with a
leading
|, indented under the declaration or parameter; on one line, a written leading|is removed. A union of one object type withnull,undefinedorvoidkeeps the object on the line ({ … } | null). Intersections break after each&. - Generics: type parameters break like call arguments, one per line
with a trailing comma (
shared.trailing_comma); type arguments break the same way without a trailing comma, and a lone type literal argument hugs the angle brackets (useState<{…}>(); Prettier). - Enums are always broken, one member per line with a trailing comma.
- Conditional types break like conditional expressions, before
?and:. - Parameters: a lone parameter typed by an object type hugs the
parentheses (
(options: {…})); with an object pattern (({ a, b }: {…})) both braces break when it does not fit. - Parameter properties: a constructor with two or more parameters,
one of them a parameter property (
private readonly db: Db), puts one parameter per line (Prettier). - Classes, namespaces and declarations (
abstract,declare,override,accessor, access modifiers, overloads,namespace,declare module,declare global) are spaced like their JavaScript counterparts; each overload signature stays on its own line. - TSX: JSX is printed as written, as in JavaScript.
<T,>(x: T) => xkeeps its comma in.tsxfiles (without it,<T>would open a JSX element); in.tsfiles it is removed.
Semicolons¶
language.typescript.semicolons works as in JavaScript for statements,
including the TypeScript ones that may end with ; (type A = B,
overload signatures, declare const x: T, export = x,
import x = require("x"), import A = B.C), and for class members:
method signatures, index signatures and abstract members get a ; with
"always". It also decides the member separators of interfaces and type
literals:
"as_needed"(default): no separator at the end of a broken member line. A;stays before a member the grammar would otherwise join to the member before: one starting with<(a generic call signature) or named likein(in,in2; see JavaScript's;guards), and, in a type literal inside type arguments (f<{…}>(), where the grammar reads more as an expression), one starting with[,(,-or+."always": a;after every member of a broken body."preserve": a;after a broken member where a separator (;or,) was written.
On one line, members are separated by ; and the last has none, in
every mode: type Point = { x: number; y: number }.
Options¶
[language.typescript] has the JavaScript options, with the same
defaults; [language.javascript] does not apply to TypeScript.
| Key | Values | Default |
|---|---|---|
language.typescript.semicolons |
"as_needed", "always", "preserve" (see above) |
"as_needed" |
language.typescript.object_wrap |
"preserve", "fit" (objects and type literals) |
"preserve" |
language.typescript.bracket_spacing |
true: { a }; false: {a} (also type literals) |
true |
language.typescript.binary_operator_break |
"after", "before" |
"after" |
shared.trailing_comma |
"never", "multiline" |
"multiline" for TypeScript |
Differences from Prettier¶
Those of JavaScript (javascript.md),
and:
- Nested conditional types are indented by a full level at each level, and a nested one stays on one line if it fits (Prettier breaks the whole chain and aligns each level by 2 columns).
- A long class head is not broken at
extendsorimplements(Prettier moves them, and the{, to lines of their own).
Limitations¶
- The grammar (tree-sitter-typescript 0.23.2) does not parse
export type * from "…", variance annotations (in T,out T),abstract overrideand optional elements after a literal type in a tuple (['a'?]); such files are reported as syntax errors and left unchanged. - Types inside JSDoc comments are comment text and not formatted.