twoslash

repository·main·Indexed 21 days ago

https://github.com/twoslashes/twoslash

A markup format for TypeScript code samples that enables the creation of self-contained, compiler-validated code snippets for documentation. It provides a low-level engine for extracting type information and includes specialized packages for CDN usage (twoslash-cdn), remote API delegation (twoslash-remote), and framework-specific support for Vue (twoslash-vue) and Svelte (twoslash-svelte). It is the community-driven successor to @typescript/twoslash.

Tokens
21.3K
Snippets
76
Records
95
Agent score
75%

What's inside twoslash

  1. What is Twoslash?

    main
    Twoslash is a markup format for TypeScript code designed for creating self-contained code samples. It allows the TypeScript compiler to perform validation and type-checking on code snippets, making it ideal for documentation and technical content where code accuracy is critical. It is inspired by the fourslash test system and is the successor to @typescript/twoslash.
  2. Synchronize Twoslash usage with prepreTypes()

    main

    Since fetching files from a CDN is asynchronous, you cannot make the entire process synchronous. However, you can separate the asynchronous type fetching from the synchronous execution.

    Use await twoslash.prepreTypes(code) to load all necessary types for a specific code snippet before performing synchronous operations (such as using a synchronous highlighter like Shiki). Once types are pre-loaded, you can use twoslash.runSync for the actual execution.

    import { transformerTwoslash } from '@shikijs/twoslash'
    import { createHighlighter } from 'shiki'
    import { createTwoslashFromCDN } from 'twoslash-cdn'
    
    const highlighter = await createHighlighter({})
    
    const twoslash = createTwoslashFromCDN()
    
    const code = `
    import { ref } from 'vue'
    const foo = ref(1)
    //    ^?
    `
    
    // Load all necessary types from CDN before hand.
    await twoslash.prepreTypes(code)
    
    // This can be done synchronously.
    const highlighted = highlighter.codeToHtml(code, {
      lang: 'ts',
      theme: 'dark-plus',
      transformers: [
        transformerTwoslash({
          // Use `twoslash.runSync` to replace the non-CDN `twoslasher` function.
          twoslasher: twoslash.runSync
        })
      ],
    })
  3. Query code with `?^` and `^|` notations

    main

    Twoslash allows you to mechanically pull information from your code using specific query sigils placed on the line below the target code.

    Extract Type (?^)

    Use ?^ to extract the type information of an identifier in the line immediately above it.

    Completions (^|)

    Use ^| to show what auto-complete results would look like at a specific location. Twoslash requests completions from TypeScript and filters them based on the characters following the .. Up to 5 results are shown inline, respecting deprecation markers.

    Highlighting (^^^)

    Use ^^^ to highlight a specific range of characters on the line above it. The exact visual style depends on your renderer (e.g., Shiki integrations often wrap these in a .twoslash-highlighted class).

    const hi = 'Hello'
    const msg = `${hi}, world`
    //    ^?
    
    // Completions example
    // @noErrors
    console.e
    //       ^|
    
    // Highlighting example
    function add(a: number, b: number) {
      //     ^^^
      return a + b
    }
  4. Use Twoslash markup for TypeScript code samples

    main

    To use Twoslash, wrap your TypeScript code in a code block with the ts twoslash language identifier. You can then use special comments to instruct the compiler to expect specific errors.

    Key Features:

    • Error Annotation: Use the // @errors: <error-code> comment to specify which TypeScript error should be triggered in the block.
    • Code Cutting: Use the // ---cut--- comment to separate code segments. This is useful when you want to reuse a variable declaration or setup from a previous block within the same Twoslash context, allowing the compiler to maintain state across the 'cut'.
    // @errors: 2322
    let x: [string, number]
    
    // Initialize it incorrectly
    x = [10, 'hello']
    
    // ---cut---
    
    // The compiler remembers 'x' from above
    console.log(x[1].substring(1))
  5. Cut code samples using sigils

    main

    To keep code samples concise while maintaining a valid, compilable TypeScript program, you can use 'cut' sigils. Twoslash processes these after generating editor information, automatically adjusting offsets and lines so that queries and highlights still work in the trimmed output.

    // ---cut-before--- or // ---cut---

    Removes everything above the sigil. Only the code below the sigil is displayed to the user.

    // ---cut-after---

    Removes everything below the sigil.

    // ---cut-start--- and // ---cut-end---

    Removes the section of code between these two sigils. You can use multiple pairs to cut out several sections.

    Note: The // @filename: [file] command is specifically designed NOT to be removed by cutting, ensuring multi-file logic remains intact if needed.

    const level: string = 'Danger'
    // ---cut---
    console.log(level)
    
    // Cutting a middle section
    const level: string = 'Danger'
    // ---cut-start---
    console.log(level) // This is not shown.
    // ---cut-end---
    console.log('This is shown')
  6. Add syntax highlighting and type information with @shikijs/twoslash

    main

    Twoslash itself is a low-level engine focused on extracting type information and does not handle syntax highlighting. To render code snippets with integrated type information (similar to the Twoslash website), use Shiki along with the @shikijs/twoslash transformer. This transformer integrates Twoslash's type information directly into your Shiki-highlighted code snippets.

    import { transformerTwoslash } from '@shikijs/twoslash'
    import { codeToHtml } from 'shiki'
    
    const html = await codeToHtml(`console.log()`, {
      lang: 'ts',
      theme: 'vitesse-dark',
      transformers: [
        transformerTwoslash(),
      ],
    })
  7. Migrate from @typescript/twoslash to twoslash

    main

    If you are migrating from the legacy @typescript/twoslash package, be aware that twoslash is a community-driven successor with improved performance and more flexible APIs. Several breaking changes exist:

    • Unified Result Structure: Instead of separate arrays for staticQuickInfo, queries, errors, and tags, all information is now unified into a single nodes array. See the Information Nodes documentation for details.
    • Entry Points: The main entry point import { ... } from "twoslash" depends on the typescript package. If you want a dependency-free version, use the twoslash/core sub-entry point, which requires you to provide your own TypeScript instance.
    • Renamed Options:
      • defaultOptions is now handbookOptions.
      • defaultCompilerOptions is now compilerOptions.
    • Compiler Defaults: The default compilerOptions.target is now "esnext" (previously "es5").
    • Removed Property: playgroundURL has been removed from the result object.