TypeScript Formatter

Format TypeScript by reindenting it with a TypeScript-aware lexer — never by rewriting its tokens, so name?: string, generics and enums survive exactly (the JavaScript engine next door provably mangles them; the comparison is on the page). One-line input is expanded structurally, and the output is verified token-for-token before it is shown. Free, no sign-up — runs locally in your browser.

…or drop a file onto the panel. Files are read locally and never uploaded.

Your work is auto-saved in this browser only, so a refresh won't lose it. Nothing is uploaded.

The TypeScript formatting problem

TypeScript's type syntax is not decoration on JavaScript — it is a second grammar woven through the first, and a formatter that only understands the first will eventually rewrite the second into something else. This is not hypothetical: the widely used js-beautify engine behind this site's JavaScript Formatter turns name?: string into name ? : string on TypeScript input — an optional property silently becoming a ternary fragment. This formatter takes the opposite approach. A TypeScript-aware lexer reads your file first (strings, template literals with nested interpolation, comments, regex literals, and the bracket structure), and formatting then changes indentation only: lines are re-based by bracket depth, one-line files are expanded structurally, and the result is verified token-for-token against the input before you ever see it. If verification fails, you get an error and your original — never a quietly altered program.

How to use this tool

  1. Paste TypeScript (or TSX-flavoured code), or upload/drop a .ts file — interfaces, generics, enums, decorators, type predicates and as casts all lex as what they are.
  2. With “Format as I type” on, the result updates as you edit; otherwise press Format TypeScript.
  3. Pick 2 spaces, 4 spaces or a tab. Indentation is the only style choice, on purpose: the rest of the layout is yours and stays yours.
  4. An error means the lexer found something it could prove is broken — an unterminated string or template, a bracket that never closes — at the line and column given. Fix it there; this tool does not guess past damage.
  5. Copy or download the result. Your input panel is never rewritten, so the original survives beside the formatted version for diffing.

Scope, stated honestly

This is a lexer-level formatter, not the TypeScript compiler, and the difference is printed here rather than discovered by you. It will not find type errors, and some grammar errors are invisible to a lexer (a missing colon inside an annotation lexes fine). What it guarantees is narrower and, for a formatter, more important: it cannot change what your code says. Token stream in, same token stream out — strings and template literals byte-for-byte, comments intact, type syntax untouched — or no output at all. For a full canonical style (quote marks, semicolon policy, intra-line spacing), run Prettier or your editor's formatter in your own toolchain; run this when you need TypeScript tidied somewhere a toolchain isn't — a snippet from a forum, a generated file, a gist — without risking the one failure mode a formatter must never have.

Worked example

This input, with its wandering indentation:

interface User {
        id: number;
     name?: string;
  tags: Array<string>;
}
function pick(map: Map<string, number>, key: string) {
       return map.get(key);
}

formats to:

interface User {
  id: number;
  name?: string;
  tags: Array<string>;
}
function pick(map: Map<string, number>, key: string) {
  return map.get(key);
}

Note what did not move: name?: string keeps its question mark welded to the name, the generics keep their tight spelling, and the interface body stays one property per line. Only the leading whitespace changed — that is the whole product.

Frequently asked questions

Why doesn't the JavaScript Formatter on this site just handle TypeScript?

Because it can't — measurably. Its engine (js-beautify's JavaScript mode) reads type syntax as expressions, and on TypeScript input it does real damage: `name?: string` comes back as `name ? : string` (an optional property rewritten into a broken ternary fragment), `Map<string, number>` is spaced out to `Map < string, number >` with line breaks inside the generics, and an interface body collapses onto one line. Those are not style differences; the first is a different, invalid program. So this tool exists separately, built on a TypeScript-aware lexer that treats type syntax as syntax. The JavaScript Formatter remains the right tool for JavaScript — it has a real parser and a canonical style. This one's engine is chosen for not corrupting TypeScript.

So what does this formatter actually change?

Whitespace between lines, and only that: indentation is recomputed from bracket depth, blank-line runs collapse to one, trailing whitespace is trimmed, and code that arrives on a single line is expanded at braces and statement ends. The spacing inside a line is never rewritten — deliberately, because in TypeScript intra-line spacing is syntax and convention at once: name?: string is not a ternary, Array<string> is not a comparison, x as string is not two expressions. Tools that 'beautify' those spaces are guessing with your program. What you get here is your code, with its layout normalised — and a guarantee, below, that nothing else moved.

How is the output guaranteed not to change my code?

By construction and by verification. Construction: the formatter only ever inserts or removes whitespace at line boundaries. Verification: before the result is shown, it is re-lexed and its token stream must equal the input's token for token — every identifier, string, template literal, comment and operator, in order. If they differ, the output is discarded and you get an error saying so (with your input untouched), instead of subtly different code. That is the same bargain the JavaScript Formatter makes with its syntax trees, applied at the level TypeScript allows without a compiler in the page.

Is this a TypeScript validator? Will it catch my type errors?

No — it is a lexer-level check, and the page won't pretend otherwise. It catches unterminated strings, template literals and block comments, and brackets that don't balance, each with a line and column. It cannot catch type errors, undefined names, or grammar slips a lexer cannot see (a missing colon in an annotation lexes perfectly). One more honest limit: a regex literal directly after a closing brace is read as division, because settling that ambiguity needs a real parser. Run tsc for truth; run this for layout and the structural errors.

What happens to template literals and comments?

They are treated as atoms: the lexer recognises them (including ${} interpolation with nested strings and templates) and their contents pass through untouched. Lines inside a template literal or a block comment are content, not layout — they are never reindented, because reindenting a template literal would change the string your program produces. Comments keep their text and their line; a // comment also ends its line in the one-line expansion mode.

My whole file is on one line. What will I get?

A structural expansion: lines break after opening braces, before closing braces, and at statement-ending semicolons outside parentheses (so for(;;) headers stay intact), then everything is indented by depth. Continuations like } else { and } catch { stay together, as they should. It is a rescue layout — readable and safe — not a full style pass: object literal members stay on their line, for instance. For a file you will keep, this gets you to the point where your editor's own formatter can take over.

Related tools