You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 19:31:05