Twoslash: Code Blocks with a Heartbeat
Code is poetry, but compiling it is reality.
Most technical writing treats code as a still life. Syntax highlighting makes it beautiful, but underneath, it remains dead text. No type checking. No language server. No context.
It looks perfect on the screen, but the moment a reader copies it into their editor, the illusion shatters. Missing imports. Type mismatches. Phantom environments. The real world of code is interconnected; a static markdown block is an agreed-upon lie.
We shouldn't just format code. We should give it a heartbeat. Enter Twoslash.
Here’s how we make it live.
- Make It Honest
- Make It Conversational
- Make It Anticipate
- Make It Connected
- Make It Pure
- Make It Cinematic
#1. Make It Honest
A good editor doesn't just display text; it interrogates it. When code is wrong, it should bleed red. By running a real TypeScript compiler under the hood, we capture actual diagnostics. No more writing fake error comments. The code block feels its own fractures.
// Type 'string' is not assignable to type 'number'.
const Type 'string' is not assignable to type 'number'.hello: number = "world"#2. Make It Conversational
Reading code is often a monologue. Twoslash makes it a dialogue. By using // ^?, you can ask the compiler what it thinks. It bridges the gap between human intent and machine inference, revealing the hidden geometry of your data.
/**
* Greets a person.
*/
function function greet(name: string): stringGreets a person.greet(name: stringname: string) {
return `Hello ${name: stringname}`
}
function greet(name: string): stringGreets a person.greet
#3. Make It Anticipate
The most intimate moment between a developer and their editor is the autocomplete dropdown. It is the machine finishing your sentence. We can bring this exact IntelliSense experience into static markdown using // ^|.
const const greeting: "world"greeting = "world"
greeting- greeting
.Identifier expected.
#4. Make It Connected
Code never exists in a vacuum. It imports, exports, and depends on a constellation of other files. Traditional snippets force you to hallucinate the file system. Twoslash spins up a virtual one. You can define multiple files in a single block, and the compiler will resolve the imports perfectly.
// @filename: utils.ts
export const const double: (n: number) => numberdouble = (n: numbern: number) => n: numbern * 2
// @filename: index.ts
import { const double: (n: number) => numberdouble } from "./utils"
function double(n: number): numberdouble(2)
const double: (n: number) => numberdouble
#5. Make It Pure
The stage needs a backstage. Often, to make a snippet compile, you need lines of tedious boilerplate, mocked data, and interface definitions. But the reader only cares about the core logic. ---cut--- acts as the curtain. The compiler processes the noise; the reader experiences only the signal.
const const shown: numbershown = const secret: 32secret + 1#6. Make It Cinematic
Sometimes you need to direct the reader's eye, like a camera lens pulling focus. Whether it's a specific line of code or a recurring variable, granular highlighting cuts through the visual noise and whispers: Look here.
const const a: 1a = 1
const const b: 2b = 2
const const c: numberc = const a: 1a + const b: 2bconst const hello: "world"hello = "world"
var console: ConsoleThe `console` module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
* A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream.
* A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and
[`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module.
_**Warning**_: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for
more information.
Example using the global `console`:
```js
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
```
Example using the `Console` class:
```js
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
```console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)Prints to `stdout` with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html)
(the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args)).
```js
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
```
See [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args) for more information.log(const hello: "world"hello)These aren't just styling tricks. They are guarantees. When your documentation is backed by a real compiler, it can never lie to your readers. It survives refactoring. It outlives API changes.
We are no longer writing static documentation. We are shipping tiny, frozen moments of a living editor.