使用TypeScript开发自定义Monaco语言Worker的类型与启动问题
Monaco Web Worker 类型丢失与启动失败问题
问题描述
我们用 TypeScript 为自定义语言开发 Monaco Web Worker,已实现可运行的 Worker,但遇到两个核心问题:
- 从
monaco-editor的 esm 目录导入模块时,TypeScript 类型信息丢失,不得不添加// @ts-ignore忽略类型报错 - 尝试从
editor.api.js导入以获取完整类型时,Worker 直接无法启动,抛出运行时错误
依赖版本:
- monaco-editor@^0.46.0
- vite@^5.2.13
类型丢失示例
// @ts-ignore import * as languages from 'monaco-editor/esm/vs/editor/common/languages'; // ... function doSomething(tokens: languages.ILineTokens): string { // ... }
JasmineWorker.ts 核心代码
// @ts-ignore import * as worker from 'monaco-editor/esm/vs/editor/editor.worker'; // @ts-ignore import * as languages from 'monaco-editor/esm/vs/editor/common/languages'; // @ts-ignore import { Position } from 'monaco-editor/esm/vs/editor/common/core/position'; import IWorkerContext = worker.IWorkerContext; // ... export class JasmineWorker { private ctx: IWorkerContext; constructor(ctx: IWorkerContext, _createData: ICreateData) { this.ctx = ctx; } public async getCompletionItems( options: AutoCompleteOptions, word: IWordAtPosition, position: Position, range: languages.CompletionItemRanges ): Promise<languages.CompletionList> { // ... } } export function create( ctx: IWorkerContext, createData: ICreateData ): JasmineWorker { return new JasmineWorker(ctx, createData); } self.onmessage = () => { worker.initialize((ctx: IWorkerContext, createData: ICreateData) => { return new JasmineWorker(ctx, createData); }); }; export default JasmineWorker;
JasmineSourceEditor.ts 核心代码
import { JasmineWorker } from './JasmineWorker'; import WorkerUrl from './JasmineWorker?worker&url'; // ... self.MonacoEnvironment = { getWorker: function (_moduleId, label) { if (label === 'jasmine') { return new Worker(new URL('./JasmineWorker.ts', import.meta.url), { type: 'module', }); } return new Worker( new URL('monaco-editor/esm/vs/editor/editor.worker.js', import.meta.url), { type: 'module' } ); }, }; export class JasmineSourceEditor { // ... private autoCompleteDisposal: IDisposable | undefined; private worker: editor.MonacoWebWorker<JasmineWorker>; constructor(parent: HTMLElement, options: EditorOptions) { this.worker = editor.createWebWorker<JasmineWorker>({ moduleId: WorkerUrl, label: 'jasmine', keepIdleModels: true, createData: {}, }); } registerCompletionItemProvider(options: AutoCompleteOptions): IDisposable { return monaco.languages.registerCompletionItemProvider('jasmine', { provideCompletionItems: async (model, position) => { await this.worker.withSyncedResources([model.uri]); const word = model.getWordUntilPosition(position); const range = { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: word.startColumn, endColumn: word.endColumn, }; const proxy = await this.worker.getProxy(); const suggestions = proxy.getCompletionItems( options, word, position, range ); return suggestions; }, }); } configureAutoComplete(options: AutoCompleteOptions): void { if (this.autoCompleteDisposal) { this.autoCompleteDisposal.dispose(); } if (options.mode !== AutoCompleteMode.Disabled) { this.autoCompleteDisposal = this.registerCompletionItemProvider(options); } console.log(`auto complete configured`); } }
尝试从 editor.api.js 导入的代码
import { editor } from 'monaco-editor/esm/vs/editor/editor.api'; import { worker } from 'monaco-editor/esm/vs/editor/editor.api'; import { languages } from 'monaco-editor/esm/vs/editor/editor.api'; import { IPosition, IRange } from 'monaco-editor/esm/vs/editor/editor.api';
报错信息
Could not create web worker(s). Falling back to loading web worker code in main thread, which might cause UI freezes. Please see https://github.com/microsoft/monaco-editor#faq
Uncaught Error: Unexpected usage Error: Unexpected usage at _EditorSimpleWorker.loadForeignModule (chunk-4C672B3V.js?v=b058a95f:9058:27) at chunk-UV56DTU5.js?v=b058a95f:29446:22 at async Object.provideCompletionItems (JasmineSourceEditor.ts:90:9) at async monaco-editor.js?v=b058a95f:28834:22 at async Promise.all (index 0) at async provideSuggestionItems (monaco-editor.js?v=b058a95f:28822:5) at async Promise.all (index 0) at chunk-7ATFQVGH.js?v=b058a95f:3090:21
解决方案
1. 正确获取类型信息(移除@ts-ignore)
monaco-editor 的 npm 包已内置完整 TypeScript 类型定义,无需直接导入 esm 模块获取类型。有两种可行方式:
- 方式一:从 monaco-editor 包导入类型
import type { languages, editor } from 'monaco-editor'; function doSomething(tokens: languages.ILineTokens): string { // ... }
- 方式二:使用全局 monaco 类型别名
type ILineTokens = monaco.languages.ILineTokens; function doSomething(tokens: ILineTokens): string { // ... }
2. 修复 Worker 启动失败问题
editor.api.js 是为主线程设计的模块,包含编辑器实例代码,不适合在 Worker 环境运行。正确做法是:
- 主线程代码(如 JasmineSourceEditor.ts)可从
monaco-editor或editor.api导入类型 - Worker 线程代码(JasmineWorker.ts)保留原 esm 路径的运行时导入,通过类型导入获取类型信息,避免打包冲突
修改 JasmineWorker.ts 的导入逻辑:
// 运行时导入(保留原路径,无需@ts-ignore) import * as worker from 'monaco-editor/esm/vs/editor/editor.worker'; import * as languages from 'monaco-editor/esm/vs/editor/common/languages'; import { Position } from 'monaco-editor/esm/vs/editor/common/core/position'; // 类型导入(从monaco-editor包获取) import type { IWorkerContext } from 'monaco-editor';
同时检查 Vite 配置:
- 确保 Worker 文件使用
?worker或?worker&url导入(你的代码已满足) - 确保 Worker 中未引用主线程专属 API(如 DOM 对象)
3. 额外注意事项
- 确保 monaco-editor@0.46.0 的 Worker 初始化逻辑正确,
self.onmessage中的worker.initialize调用无误 - 禁止在 Worker 中导入
editor.api.js,避免环境冲突
内容的提问来源于stack exchange,提问作者Takashi Matsuo
相关产品推荐
相关产品推荐

