A browser based code editor that powers Visual Studio Code
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Advanced TypeScript and JavaScript language features including full compiler API access, type checking, and IntelliSense.
import * as monaco from 'monaco-editor';
// TypeScript language service APIs
const tsDefaults = monaco.languages.typescript.typescriptDefaults;
const jsDefaults = monaco.languages.typescript.javascriptDefaults;Monaco provides separate defaults for TypeScript and JavaScript:
monaco.languages.typescript.typescriptDefaults: LanguageServiceDefaultsConfiguration for TypeScript language service.
monaco.languages.typescript.javascriptDefaults: LanguageServiceDefaultsConfiguration for JavaScript language service.
languageDefaults.setCompilerOptions(options: ts.CompilerOptions): voidSets TypeScript compiler options.
languageDefaults.getCompilerOptions(): ts.CompilerOptionsGets current compiler options.
// Configure TypeScript compiler options
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES2020,
allowNonTsExtensions: true,
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
module: monaco.languages.typescript.ModuleKind.CommonJS,
noEmit: true,
esModuleInterop: true,
jsx: monaco.languages.typescript.JsxEmit.React,
reactNamespace: "React",
allowJs: true,
typeRoots: ["node_modules/@types"]
});
// Configure JavaScript options
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES2020,
allowNonTsExtensions: true,
allowJs: true,
module: monaco.languages.typescript.ModuleKind.CommonJS,
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs
});interface CompilerOptions {
target?: ScriptTarget;
module?: ModuleKind;
moduleResolution?: ModuleResolutionKind;
allowJs?: boolean;
allowNonTsExtensions?: boolean;
noEmit?: boolean;
strict?: boolean;
esModuleInterop?: boolean;
skipLibCheck?: boolean;
jsx?: JsxEmit;
reactNamespace?: string;
baseUrl?: string;
paths?: { [key: string]: string[] };
typeRoots?: string[];
types?: string[];
// ... many more options
}languageDefaults.addExtraLib(content: string, filePath?: string): IDisposableAdds additional TypeScript library definitions.
languageDefaults.getExtraLibs(): { [path: string]: string }Gets all extra libraries.
// Add custom type definitions
const libSource = `
declare namespace MyLibrary {
interface User {
id: number;
name: string;
email: string;
}
function getUser(id: number): Promise<User>;
function createUser(data: Omit<User, 'id'>): Promise<User>;
}
`;
const libUri = 'ts:filename/mylibrary.d.ts';
monaco.languages.typescript.typescriptDefaults.addExtraLib(libSource, libUri);
// Add external library definitions
const lodashTypes = `
declare module 'lodash' {
export function debounce<T extends (...args: any[]) => any>(
func: T,
wait?: number,
options?: { leading?: boolean; maxWait?: number; trailing?: boolean }
): T & { cancel(): void; flush(): ReturnType<T> };
}
`;
monaco.languages.typescript.typescriptDefaults.addExtraLib(lodashTypes, 'node_modules/@types/lodash/index.d.ts');languageDefaults.setExtraLibs(libs: { content: string; filePath?: string }[]): voidSets the complete set of extra libraries, replacing any existing ones.
languageDefaults.setDiagnosticsOptions(options: DiagnosticsOptions): voidConfigures diagnostic behavior.
interface DiagnosticsOptions {
noSemanticValidation?: boolean;
noSyntaxValidation?: boolean;
noSuggestionDiagnostics?: boolean;
diagnosticCodesToIgnore?: number[];
}// Configure diagnostics
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
noSemanticValidation: false,
noSyntaxValidation: false,
noSuggestionDiagnostics: false,
diagnosticCodesToIgnore: [2307, 2339] // Ignore specific error codes
});
// Disable all validation for JavaScript
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
noSemanticValidation: true,
noSyntaxValidation: false
});languageDefaults.setEagerModelSync(value: boolean): voidControls whether models are synchronized eagerly or lazily.
// Enable eager model sync for better IntelliSense
monaco.languages.typescript.typescriptDefaults.setEagerModelSync(true);The TypeScript language service provides comprehensive IntelliSense features:
// Create main TypeScript file
const mainModel = monaco.editor.createModel(`
import { User } from './types';
import { api } from './api';
export class UserService {
async getUser(id: number): Promise<User> {
return api.get(\`/users/\${id}\`);
}
async createUser(userData: Omit<User, 'id'>): Promise<User> {
return api.post('/users', userData);
}
}
`, 'typescript', monaco.Uri.parse('file:///main.ts'));
// Create types file
const typesModel = monaco.editor.createModel(`
export interface User {
id: number;
name: string;
email: string;
createdAt: Date;
}
export interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
`, 'typescript', monaco.Uri.parse('file:///types.ts'));
// Create API file
const apiModel = monaco.editor.createModel(`
import { ApiResponse } from './types';
export const api = {
async get<T>(url: string): Promise<T> {
const response = await fetch(url);
const data: ApiResponse<T> = await response.json();
return data.data;
},
async post<T>(url: string, body: any): Promise<T> {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
const data: ApiResponse<T> = await response.json();
return data.data;
}
};
`, 'typescript', monaco.Uri.parse('file:///api.ts'));// Enable JSX support
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
jsx: monaco.languages.typescript.JsxEmit.React,
reactNamespace: "React",
allowJs: true,
esModuleInterop: true
});
// Add React types
const reactTypes = `
declare namespace React {
interface Component<P = {}, S = {}> {}
interface FunctionComponent<P = {}> {
(props: P): JSX.Element | null;
}
type FC<P = {}> = FunctionComponent<P>;
}
declare namespace JSX {
interface Element {}
interface IntrinsicElements {
div: any;
span: any;
button: any;
input: any;
// ... other HTML elements
}
}
`;
monaco.languages.typescript.typescriptDefaults.addExtraLib(reactTypes, 'node_modules/@types/react/index.d.ts');// Create React component model
const componentModel = monaco.editor.createModel(`
import React from 'react';
interface Props {
name: string;
age: number;
onUpdate: (name: string) => void;
}
export const UserCard: React.FC<Props> = ({ name, age, onUpdate }) => {
const [editing, setEditing] = React.useState(false);
const [currentName, setCurrentName] = React.useState(name);
const handleSave = () => {
onUpdate(currentName);
setEditing(false);
};
return (
<div className="user-card">
{editing ? (
<input
value={currentName}
onChange={(e) => setCurrentName(e.target.value)}
onBlur={handleSave}
/>
) : (
<span onClick={() => setEditing(true)}>
{name} ({age} years old)
</span>
)}
</div>
);
};
`, 'typescript', monaco.Uri.parse('file:///UserCard.tsx'));// Configure path mapping
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
baseUrl: '.',
paths: {
'@/*': ['src/*'],
'@components/*': ['src/components/*'],
'@utils/*': ['src/utils/*'],
'@types/*': ['src/types/*']
}
});
// Add corresponding library definitions
monaco.languages.typescript.typescriptDefaults.addExtraLib(`
declare module '@utils/helpers' {
export function formatDate(date: Date): string;
export function validateEmail(email: string): boolean;
}
declare module '@types/api' {
export interface ApiError {
code: number;
message: string;
details?: any;
}
}
`, 'file:///node_modules/@types/project/index.d.ts');// Add global type definitions
const globalTypes = `
declare global {
interface Window {
gtag: (command: string, ...args: any[]) => void;
dataLayer: object[];
}
var process: {
env: {
NODE_ENV: 'development' | 'production' | 'test';
API_URL: string;
};
};
}
export {};
`;
monaco.languages.typescript.typescriptDefaults.addExtraLib(globalTypes, 'file:///globals.d.ts');The TypeScript language service runs in a web worker for better performance:
// The worker is automatically configured, but you can customize it
self.MonacoEnvironment = {
getWorkerUrl: function (moduleId, label) {
if (label === 'typescript' || label === 'javascript') {
return './ts.worker.bundle.js';
}
return './editor.worker.bundle.js';
}
};monaco.languages.typescript.getTypeScriptWorker(): Promise<TypeScriptWorker>Gets the TypeScript worker for advanced operations.
monaco.languages.typescript.getJavaScriptWorker(): Promise<TypeScriptWorker>Gets the JavaScript worker.
// Access TypeScript language service directly
monaco.languages.typescript.getTypeScriptWorker().then(worker => {
return worker(uri).then(client => {
// Direct access to TypeScript compiler API methods
client.getSyntacticDiagnostics(uri.toString()).then(diagnostics => {
console.log('Syntax errors:', diagnostics);
});
client.getSemanticDiagnostics(uri.toString()).then(diagnostics => {
console.log('Type errors:', diagnostics);
});
client.getCompletionsAtPosition(uri.toString(), position).then(completions => {
console.log('Completions:', completions);
});
});
});interface TypeScriptWorker {
getSyntacticDiagnostics(fileName: string): Promise<ts.Diagnostic[]>;
getSemanticDiagnostics(fileName: string): Promise<ts.Diagnostic[]>;
getSuggestionDiagnostics(fileName: string): Promise<ts.DiagnosticWithLocation[]>;
getCompletionsAtPosition(fileName: string, position: number): Promise<ts.CompletionInfo>;
getSignatureHelpItems(fileName: string, position: number): Promise<ts.SignatureHelpItems>;
getQuickInfoAtPosition(fileName: string, position: number): Promise<ts.QuickInfo>;
getDefinitionAtPosition(fileName: string, position: number): Promise<ts.DefinitionInfoAndBoundSpan>;
getReferencesAtPosition(fileName: string, position: number): Promise<ts.ReferenceEntry[]>;
getFormattingEditsForDocument(fileName: string, options: ts.FormatCodeOptions): Promise<ts.TextChange[]>;
getEmitOutput(fileName: string): Promise<ts.EmitOutput>;
}Usage Example:
async function getTypeScriptDiagnostics(model: monaco.editor.ITextModel) {
const worker = await monaco.languages.typescript.getTypeScriptWorker();
const client = await worker(model.uri);
// Get syntax errors
const syntaxErrors = await client.getSyntacticDiagnostics(model.uri.toString());
// Get type errors
const typeErrors = await client.getSemanticDiagnostics(model.uri.toString());
// Get suggestions
const suggestions = await client.getSuggestionDiagnostics(model.uri.toString());
return { syntaxErrors, typeErrors, suggestions };
}// Dispose models when no longer needed
model.dispose();
// Clear extra libs if needed
monaco.languages.typescript.typescriptDefaults.setExtraLibs([]);
// Manage large codebases efficiently
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
noSemanticValidation: false,
noSyntaxValidation: false,
diagnosticCodesToIgnore: [] // Only ignore specific errors, not all
});// Load type definitions on demand
async function loadTypeDefinitions(libraryName: string) {
try {
const response = await fetch(`/types/${libraryName}.d.ts`);
const content = await response.text();
monaco.languages.typescript.typescriptDefaults.addExtraLib(
content,
`node_modules/@types/${libraryName}/index.d.ts`
);
} catch (error) {
console.warn(`Failed to load types for ${libraryName}:`, error);
}
}
// Load on first use
monaco.languages.onLanguage('typescript', () => {
loadTypeDefinitions('lodash');
loadTypeDefinitions('react');
loadTypeDefinitions('express');
});