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

如何在Monaco Editor中自动包裹用户代码进行TypeScript验证?

解决方案:在@monaco-editor/react中实现函数内部代码的TypeScript验证

更可靠的实现思路

不用手动调用TS Worker做自定义验证,而是通过双模型同步+诊断映射的方式实现:

  • 一个可见模型:供用户编辑函数内部代码
  • 一个隐藏模型:自动包裹用户代码为完整TS函数,用于真实的TypeScript验证
  • 同步两个模型的内容变化,将隐藏模型的诊断信息映射到可见模型的对应位置,再手动设置标记

代码实现

import { useEffect, useRef } from 'react';
import { Editor } from '@monaco-editor/react';
import * as monaco from 'monaco-editor';

// 定义函数包裹模板
const WRAPPER_TEMPLATE = `function wrapper(): number {
    {{USER_CODE}}
}`;

export default function FunctionCodeEditor() {
  const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null);
  const hiddenModelRef = useRef<monaco.editor.ITextModel | null>(null);

  useEffect(() => {
    if (!editorRef.current) return;

    // 初始化TS编译选项,开启严格校验
    monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
      strict: true,
      noImplicitReturns: true,
      target: monaco.languages.typescript.ScriptTarget.ESNext,
    });

    // 创建隐藏模型,用于TS后台验证
    hiddenModelRef.current = monaco.editor.createModel(
      WRAPPER_TEMPLATE.replace('{{USER_CODE}}', ''),
      'typescript',
      monaco.Uri.parse('inmemory://wrapper.ts')
    );

    const visibleModel = editorRef.current.getModel();
    if (visibleModel) {
      // 给可见模型设置自定义语言ID,避免触发默认TS验证
      visibleModel.setLanguage('custom-ts');
      // 复制TS的语法高亮规则到自定义语言,保持编辑体验
      monaco.languages.setMonarchTokensProvider('custom-ts', monaco.languages.typescript.typescriptDefaults.getMonarchTokensProvider());
    }

    // 监听隐藏模型的诊断更新,同步到可见模型
    const disposeDiagnosticsListener = monaco.languages.typescript.typescriptDefaults.onDidChangeDiagnostics(() => {
      if (!hiddenModelRef.current || !visibleModel) return;

      const diagnostics = monaco.languages.typescript.typescriptDefaults.getDiagnostics(hiddenModelRef.current);
      // 映射诊断位置:隐藏模型前两行是函数定义,行号减2;函数内部有4空格缩进,列号减4
      const mappedDiagnostics = diagnostics.map(diag => ({
        ...diag,
        startLineNumber: Math.max(1, diag.startLineNumber - 2),
        endLineNumber: Math.max(1, diag.endLineNumber - 2),
        startColumn: Math.max(1, diag.startColumn - 4),
        endColumn: Math.max(1, diag.endColumn - 4),
      })).filter(diag => diag.startLineNumber <= visibleModel.getLineCount());

      // 给可见模型设置自定义错误标记
      monaco.editor.setModelMarkers(visibleModel, 'custom-ts', mappedDiagnostics);
    });

    // 监听用户编辑,同步内容到隐藏模型
    const disposeContentChangeListener = visibleModel?.onDidChangeContent(() => {
      if (!hiddenModelRef.current || !visibleModel) return;
      const userCode = visibleModel.getValue();
      const wrappedCode = WRAPPER_TEMPLATE.replace('{{USER_CODE}}', userCode);
      hiddenModelRef.current.setValue(wrappedCode);
    });

    return () => {
      disposeDiagnosticsListener.dispose();
      disposeContentChangeListener?.dispose();
      hiddenModelRef.current?.dispose();
    };
  }, []);

  return (
    <Editor
      ref={editorRef}
      defaultLanguage="custom-ts"
      defaultValue=""
      theme="vs-dark"
      options={{
        minimap: { enabled: false },
        lineNumbers: 'on',
      }}
    />
  );
}

你遇到的问题原因解析

  1. 默认验证覆盖自定义标记:
    Monaco的TS验证是异步触发的,内容变化后会自动运行并覆盖模型标记。之前的延迟设置只是临时规避,根本解决方法是给可见模型设置自定义语言ID,完全绕过默认TS验证逻辑,由自己控制标记的生成。

  2. return未检测返回类型错误:
    一是临时模型的TS编译选项未开启严格校验(比如strict、noImplicitReturns未设为true),TS不会强制检查返回值类型;二是手动调用TS Worker时可能未等待完整的语义分析完成就获取诊断,导致部分错误遗漏。双模型方案通过监听onDidChangeDiagnostics事件,确保拿到完整的诊断结果。

内容的提问来源于stack exchange,提问作者Altair

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 03:13:10