Skip to content

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 with null, undefined or void keeps 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) => x keeps its comma in .tsx files (without it, <T> would open a JSX element); in .ts files 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 like in (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 extends or implements (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 override and 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.