Skip to content

TypeScript Snippets in Astro: Show, Don't Tell

Updated: at 

Elevate Your Astro Code Highlights with TypeScript Snippets

Want to take your Astro code highlights to the next level? This guide will show you how to implement TypeScript snippets with hover-over type information, making your code examples more interactive and informative.

Prerequisites for Astro Code Highlights

Before diving into advanced Astro code highlights, ensure you have an Astro project set up. If you need assistance, refer to the official Astro quickstart guide.

Configuring Shiki for Enhanced Astro Code Highlights

Astro comes with Shiki built-in, providing excellent syntax highlighting. Let’s optimize it for our TypeScript snippets:

  1. Modify your astro.config.mjs:
import { function defineConfig(config: AstroUserConfig): AstroUserConfig
See the full Astro Configuration API Documentation https://astro.build/config
defineConfig
} from "astro/config";
export default function defineConfig(config: AstroUserConfig): AstroUserConfig
See the full Astro Configuration API Documentation https://astro.build/config
defineConfig
({
AstroUserConfig.markdown?: {
    shikiConfig?: Partial<ShikiConfig>;
    syntaxHighlight?: "shiki" | "prism" | false;
    remarkPlugins?: RemarkPlugins;
    rehypePlugins?: RehypePlugins;
    gfm?: boolean;
    smartypants?: boolean;
    remarkRehype?: RemarkRehype;
} | undefined
@docs@kindheading@nameMarkdown Options
markdown
: {
shikiConfig?: Partial<ShikiConfig> | undefined
@docs@namemarkdown.shikiConfig@typeraw{Partial<ShikiConfig>}@descriptionShiki configuration options. See [the Markdown configuration docs](https://docs.astro.build/en/guides/markdown-content/#shiki-configuration) for usage.
shikiConfig
: {
themes?: Record<string, ThemePresets | ThemeRegistration | ThemeRegistrationRaw> | undefinedthemes: { light: "min-light"light: "min-light", dark: "tokyo-night"dark: "tokyo-night" }, }, }, });
  1. Add a stylish border to your code blocks:
pre:has(code) {
  @apply border border-skin-line;
}

Integrating Twoslash for Interactive Type Information

To make your Astro code highlights truly interactive, we’ll add Twoslash:

  1. Install the necessary packages:
npm i -D twoslash twoslash-vue
  1. Update your astro.config.mjs to include Twoslash:
import { function createTransformerFactory(defaultTwoslasher: TwoslashShikiFunction | TwoslashGenericFunction, defaultRenderer?: TwoslashRenderer): (options?: TransformerTwoslashOptions) => ShikiTransformercreateTransformerFactory, function rendererRich(options?: RendererRichOptions): TwoslashRenderer
An alternative renderer that providers better prefixed class names, with syntax highlight for the info text.
rendererRich
} from '@shikijs/twoslash/core'
import { function createTwoslasher(createOptions?: CreateTwoslashVueOptions): TwoslashInstance
Create a twoslasher instance that add additional support for Vue SFC.
createTwoslasher
} from 'twoslash-vue'
import { function defineConfig(config: AstroUserConfig): AstroUserConfig
See the full Astro Configuration API Documentation https://astro.build/config
defineConfig
} from "astro/config";
export default function defineConfig(config: AstroUserConfig): AstroUserConfig
See the full Astro Configuration API Documentation https://astro.build/config
defineConfig
({
AstroUserConfig.markdown?: {
    shikiConfig?: Partial<ShikiConfig>;
    syntaxHighlight?: "shiki" | "prism" | false;
    remarkPlugins?: RemarkPlugins;
    rehypePlugins?: RehypePlugins;
    gfm?: boolean;
    smartypants?: boolean;
    remarkRehype?: RemarkRehype;
} | undefined
@docs@kindheading@nameMarkdown Options
markdown
: {
shikiConfig?: Partial<ShikiConfig> | undefined
@docs@namemarkdown.shikiConfig@typeraw{Partial<ShikiConfig>}@descriptionShiki configuration options. See [the Markdown configuration docs](https://docs.astro.build/en/guides/markdown-content/#shiki-configuration) for usage.
shikiConfig
: {
themes?: Record<string, ThemePresets | ThemeRegistration | ThemeRegistrationRaw> | undefinedthemes: { light: "min-light"light: "min-light", dark: "tokyo-night"dark: "tokyo-night" }, transformers?: ShikiTransformer[] | undefinedtransformers: [ function createTransformerFactory(defaultTwoslasher: TwoslashShikiFunction | TwoslashGenericFunction, defaultRenderer?: TwoslashRenderer): (options?: TransformerTwoslashOptions) => ShikiTransformercreateTransformerFactory(function createTwoslasher(createOptions?: CreateTwoslashVueOptions): TwoslashInstance
Create a twoslasher instance that add additional support for Vue SFC.
createTwoslasher
())({
TransformerTwoslashOptions.langs?: string[] | undefined
Languages to apply this transformer to
langs
: ['ts', 'tsx', 'vue'],
TransformerTwoslashOptions.renderer?: TwoslashRenderer | undefined
Custom renderers to decide how each info should be rendered
renderer
: function rendererRich(options?: RendererRichOptions): TwoslashRenderer
An alternative renderer that providers better prefixed class names, with syntax highlight for the info text.
rendererRich
({
RendererRichOptions.lang?: string | undefined
Language for syntax highlight.
@defaultthe language of the code block
lang
: 'ts',
}), }), ], }, }, });

Showcasing Interactive TypeScript Snippets in Astro

Now you can create dynamic TypeScript snippets in your Markdown files:

interface User {
  User.name: stringname: string;
  User.age: numberage: number;
}

const const user: Useruser: User = {
  User.name: stringname: "John Doe",
  User.age: numberage: 30
};

var console: Console
The `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-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without calling `require('console')`. _**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-v22.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 ```
@see[source](https://github.com/nodejs/node/blob/v22.x/lib/console.js)
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-v22.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-v22.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
(
const user: User
user
.User.name: stringname);

The // ^? marker indicates where type information will appear on hover, enhancing your Astro code highlights.

Vue snippets are also supported:

<script setup lang="ts">
import { function ref<T>(value: T): [T] extends [Ref] ? IfAny<T, Ref<T>, T> : Ref<UnwrapRef<T>, UnwrapRef<T> | T> (+1 overload)
Takes an inner value and returns a reactive and mutable ref object, which has a single property `.value` that points to the inner value.
@paramvalue - The object to wrap in the ref.@see{@link https://vuejs.org/api/reactivity-core.html#ref}
ref
} from 'vue'
const { const title: stringtitle } =
const defineProps: <{
    title: string;
}>() => DefineProps<LooseRequired<{
    title: string;
}>, never> (+2 overloads)
Vue `<script setup>` compiler macro for declaring component props. The expected argument is the same as the component `props` option. Example runtime declaration: ```js // using Array syntax const props = defineProps(['foo', 'bar']) // using Object syntax const props = defineProps({ foo: String, bar: { type: Number, required: true } }) ``` Equivalent type-based declaration: ```ts // will be compiled into equivalent runtime declarations const props = defineProps<{ foo?: string bar: number }>() ```
@see{@link https://vuejs.org/api/sfc-script-setup.html#defineprops-defineemits}This is only usable inside `<script setup>`, is compiled away in the output and should **not** be actually called at runtime.
defineProps
<{
title: stringtitle: string }>() const const count: Ref<number, number>count = ref<number>(value: number): Ref<number, number> (+1 overload)
Takes an inner value and returns a reactive and mutable ref object, which has a single property `.value` that points to the inner value.
@paramvalue - The object to wrap in the ref.@see{@link https://vuejs.org/api/reactivity-core.html#ref}
ref
(0)
const const increment: () => voidincrement = () => { const count: Ref<number, number>count.
  • value
v
} const const decrement: () => voiddecrement = () => { const count: Ref<number, number>count.Ref<number, number>.value: numbervalue-- } </script> <template> <div: HTMLAttributes & ReservedPropsdiv> <h2: HTMLAttributes & ReservedPropsh2>{{ const title: stringtitle }}</h2: HTMLAttributes & ReservedPropsh2> <p: HTMLAttributes & ReservedPropsp>Count: {{ const count: Ref<number, number>count }}</p: HTMLAttributes & ReservedPropsp> <button: ButtonHTMLAttributes & ReservedPropsbutton @onClick?: ((payload: MouseEvent) => void) | undefinedclick="const increment: () => voidincrement">Increment</button: ButtonHTMLAttributes & ReservedPropsbutton> <button: ButtonHTMLAttributes & ReservedPropsbutton @onClick?: ((payload: MouseEvent) => void) | undefinedclick="const decrement: () => voiddecrement">Decrement</button: ButtonHTMLAttributes & ReservedPropsbutton> </div: HTMLAttributes & ReservedPropsdiv> </template>

For autocompletion suggestions, use the ^| marker:

const increment = () => {
  count.v
  //    ^|
}

Benefits of Enhanced Astro Code Highlights

By implementing these features, your Astro site now boasts:

These improvements significantly enhance code readability and user experience, making your Astro code highlights more valuable to your readers.

Related Posts