如何在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', }} /> ); }
你遇到的问题原因解析
默认验证覆盖自定义标记:
Monaco的TS验证是异步触发的,内容变化后会自动运行并覆盖模型标记。之前的延迟设置只是临时规避,根本解决方法是给可见模型设置自定义语言ID,完全绕过默认TS验证逻辑,由自己控制标记的生成。return未检测返回类型错误:
一是临时模型的TS编译选项未开启严格校验(比如strict、noImplicitReturns未设为true),TS不会强制检查返回值类型;二是手动调用TS Worker时可能未等待完整的语义分析完成就获取诊断,导致部分错误遗漏。双模型方案通过监听onDidChangeDiagnostics事件,确保拿到完整的诊断结果。
内容的提问来源于stack exchange,提问作者Altair
相关产品推荐
相关产品推荐

