Skip to main content

IntelliSense

The Monaco code editor provides a rich experience with intellisense and autocompletion. Many of the features required for this are based on TypeScript types that are either inferred by the editor or supplied as data definition files.

This not only works when the editor language is TypeScript, but also works with others like JavaScript and JSX.

Example:

LiveCodes Autocomplete

Types for imported npm packages

LiveCodes will try to automatically find type definitions for npm modules imported in the editor.

These are examples for automatically loading React types with autocomplete and hover info:

LiveCodes Intellisense

LiveCodes Intellisense

Custom Types

If no type definitions are found, or if you want to provide your own (e.g. for a module that is not hosted on npm), custom type definition files can be used.

In the standalone app, these can be provided in custom settings using the types property. This takes an object with the key representing the module name and the value representing the URL of the file.

Example:

Custom Settings
{
"types": {
"my-module": "https://cdn.jsdelivr.net/npm/[email protected]/types/my-module.d.ts",
"my-other-module": "https://my-website.com/my-other-module/my-other-module.d.ts"
}
}

For embedded playgrounds, these can be provided in the configuration object using the types property.

This can be combined with the imports property to provide importmap for runtime implementation of your custom modules.

This is an example of how to create a playground that provides the implementation of the custom module: my-module and its type definition to provide editor intellisense:

import { createPlayground } from 'livecodes';

const config = {
activeEditor: 'script',
script: {
language: 'javascript',
content: `import { foo } from 'my-module';\n\nconsole.log(foo());`
};
imports: {
'my-module': 'https://my-website.com/my-module/index.js',
},
types: {
'my-module': 'https://my-website.com/my-module/my-module.d.ts',
},
};

createPlayground('#container', {config});

Please note that the URLs used for types and imports properties may be full URLs or data URLs.

This can be of great use for library authors who want to provide playgrounds for documenting their libraries that are not (yet) published to npm.

Demo

Let's assume we have this TypeScript module:

Greeter.ts
export class Greeter {
private morningGreetings = ['Good morning', 'Have a good day', 'How are you today?'];
private eveningGreetings = ['Good evening', 'Good night', 'Sleep well'];

private randomSelector(array: string[]) {
return array[Math.floor(Math.random() * array.length)];
}

public morning() {
return this.randomSelector(this.morningGreetings);
}

public evening() {
return this.randomSelector(this.eveningGreetings);
}
}

which compiles to this JavaScript:

Greeter.js
export class Greeter {
constructor() {
this.morningGreetings = ['Good morning', 'Have a good day', 'How are you today?'];
this.eveningGreetings = ['Good evening', 'Good night', 'Sleep well'];
}
randomSelector(array) {
return array[Math.floor(Math.random() * array.length)];
}
morning() {
return this.randomSelector(this.morningGreetings);
}
evening() {
return this.randomSelector(this.eveningGreetings);
}
}

and this type definition:

Greeter.d.ts
export declare class Greeter {
private morningGreetings;
private eveningGreetings;
private randomSelector;
morning(): string;
evening(): string;
}

The JavaScript output (Greeter.js) and the data definition file (Greeter.d.ts) should be hosted online or converted to data URLs (see assets and data URLs).

Then, they can be used like that:

show code
import { createPlayground } from 'livecodes';

const options = {
"config": {
"activeEditor": "script",
"script": {
"language": "typescript",
"content": "import { Greeter } from 'my-greeter';\n\nconst greeter = new Greeter();\n// now `greeter` has autocomplete\n\ndocument.body.innerText = greeter.morning();\n\n// this should show error in the editor\n// Property 'morningGreetings' is private and only accessible within class 'Greeter'\nconsole.log(greeter.morningGreetings);"
},
"imports": {
"my-greeter": "data:text/javascript;charset=UTF-8;base64,ZXhwb3J0IGNsYXNzIEdyZWV0ZXIgew0KICAgIGNvbnN0cnVjdG9yKCkgew0KICAgICAgICB0aGlzLm1vcm5pbmdHcmVldGluZ3MgPSBbJ0dvb2QgbW9ybmluZycsICdIYXZlIGEgZ29vZCBkYXknLCAnSG93IGFyZSB5b3UgdG9kYXk/J107DQogICAgICAgIHRoaXMuZXZlbmluZ0dyZWV0aW5ncyA9IFsnR29vZCBldmVuaW5nJywgJ0dvb2QgbmlnaHQnLCAnU2xlZXAgd2VsbCddOw0KICAgIH0NCiAgICByYW5kb21TZWxlY3RvcihhcnJheSkgew0KICAgICAgICByZXR1cm4gYXJyYXlbTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogYXJyYXkubGVuZ3RoKV07DQogICAgfQ0KICAgIG1vcm5pbmcoKSB7DQogICAgICAgIHJldHVybiB0aGlzLnJhbmRvbVNlbGVjdG9yKHRoaXMubW9ybmluZ0dyZWV0aW5ncyk7DQogICAgfQ0KICAgIGV2ZW5pbmcoKSB7DQogICAgICAgIHJldHVybiB0aGlzLnJhbmRvbVNlbGVjdG9yKHRoaXMuZXZlbmluZ0dyZWV0aW5ncyk7DQogICAgfQ0KfQ0K"
},
"types": {
"my-greeter": "data:text/typescript;charset=UTF-8;base64,ZXhwb3J0IGRlY2xhcmUgY2xhc3MgR3JlZXRlciB7DQogIHByaXZhdGUgbW9ybmluZ0dyZWV0aW5nczsNCiAgcHJpdmF0ZSBldmVuaW5nR3JlZXRpbmdzOw0KICBwcml2YXRlIHJhbmRvbVNlbGVjdG9yOw0KICBtb3JuaW5nKCk6IHN0cmluZzsNCiAgZXZlbmluZygpOiBzdHJpbmc7DQp9DQo="
}
}
};
createPlayground('#container', options);