此页内容

约 1427 字大约 5 分钟

2024-01-12

twoslash 实验性

为代码块添加支持 TypeScript TwoSlash 支持。 在代码块内提供内联类型提示。

该功能由 shiki@shikijs/twoslash 提供支持,并整合在 @vuepress-plume/plugin-shikiji 中。

重要

该功能当前仅在以下版本中通过可用性验证,在后续的所有的版本中均不能保证其可用性,请谨慎使用:

概述

twoslash 是一种 javascripttypescript 标记语言。 你可以编写一个代码示例来描述整个 javascript 项目。

twoslash双斜杠 作为 代码示例的预处理器。

twoslash 使用与文本编辑器相同的编译器 API 来提供类型驱动的悬停信息、准确的错误和类型标注。

功能预览

```ts twoslash
import { getHighlighterCore } from 'shiki/core'

const highlighter = await getHighlighterCore({})
//      ^?


// @log: Custom log message
const a = 1
// @error: Custom error message
const b = 1
// @warn: Custom warning message
const c = 1
// @annotate: Custom annotation message
```

将鼠标悬停在 highlighter 变量上查看效果:

import { function getHighlighterCore(options?: HighlighterCoreOptions): Promise<HighlighterCore>
Create a Shiki core highlighter instance, with no languages or themes bundled. Wasm and each language and theme must be loaded manually.
@seehttp://shiki.style/guide/install#fine-grained-bundle
getHighlighterCore
} from 'shiki/core'
const
const highlighter: HighlighterCore
highlighter
= await function getHighlighterCore(options?: HighlighterCoreOptions | undefined): Promise<HighlighterCore>
Create a Shiki core highlighter instance, with no languages or themes bundled. Wasm and each language and theme must be loaded manually.
@seehttp://shiki.style/guide/install#fine-grained-bundle
getHighlighterCore
({})
const const a: 1a = 1
Custom log message
const const b: 1b = 1
Custom error message
const const c: 1c = 1
Custom warning message
Custom annotation message

开启功能

注意

启用该功能需要对 @vuepress/markdown 包的源码进行修改,此操作具有一定的风险。

由于需要通过 pnpm patch@vuepress/markdown 包进行补丁。因此,你需要使用 pnpm 作为你的项目的包管理器。

步骤一

在项目根目录下,通过 pnpm patch 命令对 @vuepress/markdown 包进行补丁:

$ pnpm patch @vuepress/markdown --edit-dir _tmp/vuepress__markdown

该命令将会在你的项目根目录下生成一个 _tmp/vuepress__markdown 目录,该目录将会包含 @vuepress/markdown 包的源码。

步骤二

修改 @vuepress/markdown 包的源码, 打开 _tmp/vuepress__markdown/dist/index.js 文件, 找到 270 行,其内容如下:

const code = options.highlight?.(token.content, language.name, "") || md.utils.escapeHtml(token.content);

对其进行修改:

const code = options.highlight?.(token.content, language.name, "") || md.utils.escapeHtml(token.content);
const code = options.highlight?.(token.content, language.name, info || "") || md.utils.escapeHtml(token.content);

你可以直接复制上面的代码内容,然后粘贴到 _tmp/vuepress__markdown/dist/index.js 文件中替换 248 行。

步骤三

将源码修改进行 补丁提交,执行下面的命令:

$ pnpm patch-commit '_tmp/vuepress__markdown'

此命令将会在你的项目根目录下生成一个 patch 目录,该目录将会包含 @vuepress/markdown 包的补丁, 并提供 patchedDependencies 字段注册到你的项目中。

之后你就可以选择 删除 _tmp/vuepress__markdown 目录了。

使用

启用该功能后,你只需要在 原有的 markdown 代码块语法中,在代码语言声明后添加 twoslash 关键词即可:

```ts twoslash
const a = 1
```

主题仅会对有 twoslash 关键词的代码进行编译处理。

语法参考

完整语法请参考 ts-twoslashershikijs-twoslash

twoslash双斜杠 视为代码示例的预处理器。 因此,所有的标记都是在 // 之后添加的。

常用的 twoslash 标记:

^?

^? 用于突出显示类型,而无需用户进行悬停交互:

```ts twoslash
const a = 1
//    ^?
```
const 
const a: 1
a
= 1
//

需要注意的是,^必须正确指向需要突出显示类型的变量

^|

^| 可以将展示代码编辑过程中的内容预测列表,如编辑器中的自动完成功能:

```ts twoslash
// @noErrors
// @esModuleInterop
import express from "express"
const app = express()
app.get("/", function (req, res) {
  res.sen
//      ^|
})
app.listen(3000)
```
import function express(): core.Express
Creates an Express application. The express() function is a top-level function exported by the express module.
express
from "express"
const const app: Expressapp = function express(): Express
Creates an Express application. The express() function is a top-level function exported by the express module.
express
()
const app: Expressapp.Application<Record<string, any>>.get: <"/", {}, any, any, QueryString.ParsedQs, Record<string, any>>(path: "/", ...handlers: RequestHandler<{}, any, any, QueryString.ParsedQs, Record<string, any>>[]) => Express (+5 overloads)get("/", function (req: Request<{}, any, any, QueryString.ParsedQs, Record<string, any>>req, res: Response<any, Record<string, any>, number>res) { res: Response<any, Record<string, any>, number>res.se
  • send
  • sendDate
  • sendFile
  • sendStatus
  • set
  • setDefaultEncoding
  • setHeader
  • setMaxListeners
  • setTimeout
  • sendfile
n
}) const app: Expressapp.Application<Record<string, any>>.listen(port: number, callback?: (() => void) | undefined): Server<typeof IncomingMessage, typeof ServerResponse> (+5 overloads)
Listen for connections. A node `http.Server` is returned, with this application (which is a `Function`) as its callback. If you wish to create both an HTTP and HTTPS server you may do so with the "http" and "https" modules as shown here: var http = require('http') , https = require('https') , express = require('express') , app = express(); http.createServer(app).listen(80); https.createServer({ ... }, app).listen(443);
listen
(3000)
// //

需要注意的是,^必须正确指向需要进行内容预测的位置

这些类型提示并不是凭空而来的,它依赖于你的项目中的 node_modules。比如 express。 你需要在项目中 安装 @types/express 才能使用这些类型提示。

@errors

@errors: <error code> 显示代码是如何出现错误的:

```ts twoslash
// @errors: 2339
const welcome = "Tudo bem gente?"
const words = welcome.contains(" ")
```
const const welcome: "Tudo bem gente?"welcome = "Tudo bem gente?"
const const words: anywords = const welcome: "Tudo bem gente?"welcome.contains(" ")
Property 'contains' does not exist on type '"Tudo bem gente?"'.

你需要在 @errors 后面,声明对应的 typescript 错误码。

如果你不知道应该添加哪个 错误码,你可以先尝试直接编写好代码,然后等待编译失败, 你应该能够在控制台中查看到相关的错误信息,然后在错误信息的 description 中找到对应的错误码。 然后再将错误码添加到 @errors

---cut---

---cut--- 用于将代码分割, 被 ---cut--- 标记的,在其之前的代码将被忽略,不会显示:

```ts twoslash
const hi = 'hi'
// ---cut---
const msg = `${hi} words` as const
//    ^?
```
const 
const msg: "hi words"
msg
= `${const hi: "hi"hi} words` as type const = "hi words"const
//

自定义输出信息

@log, @error, @warn@annotate 用于向用户输出不同级别的自定义信息

```ts twoslash
// @log: Custom log message
const a = 1
// @error: Custom error message
const b = 1
// @warn: Custom warning message
const c = 1
// @annotate: Custom annotation message
```
const const a: 1a = 1
Custom log message
const const b: 1b = 1
Custom error message
const const c: 1c = 1
Custom warning message
Custom annotation message

import_files

@filename: <filename> 用于声明后续的代码将来自哪个文件, 你可以在其他部分的代码中通过 import 导入该文件。

```ts twoslash
// @filename: sum.ts
export function sum(a: number, b: number): number {
  return a + b
}

// @filename: ok.ts
import { sum } from "./sum"
sum(1, 2)

// @filename: error.ts
// @errors: 2345
import { sum } from "./sum"
sum(4, "woops")
```
// @filename: sum.ts
export function function sum(a: number, b: number): numbersum(a: numbera: number, b: numberb: number): number {
  return a: numbera + b: numberb
}

// @filename: ok.ts
import { function sum(a: number, b: number): numbersum } from "./sum"
function sum(a: number, b: number): numbersum(1, 2)

// @filename: error.ts
import { function sum(a: number, b: number): numbersum } from "./sum"
function sum(a: number, b: number): numbersum(4, "woops")
Argument of type 'string' is not assignable to parameter of type 'number'.

注意

在本节中未提及的其他标记,由于未正式验证其是否可用,请谨慎使用。