Adding JavaScript type hints for VSCode/Monaco Intellisence
Asked Answered
R

4

27

Is there a way to hint to VSCode/Monaco's intellisense the types of variables.

I have some code like this

var loc = window.location;
var gl = context1.getContext("webgl");
var ctx = context2.getContext("2d");

I see that VSCode knows that loc is a URL

vscode knows loc

But it doesn't know what gl is

vscode does not know gl

Nor does it know what ctx is

vscode does not know ctx

Which makes sense, having a function return different types based on its input is a somewhat unusual case.

But it does have type data for WebGLRenderingContext

vscode knows webglrenderingcontext

and it knows CanvasRenderingContext2D

vscode knows canvasrenderingcontext2d

Is there a way to for me to tell vscode/monaco that gl is an instance of WebGLRenderingContext and that ctx is an instance of CanvasRenderingContext2D without having to switch to typescript? Maybe by adding some kind of comment?

I need the solution to work in monaco (which at least in my tests shows all the same completions) because this is for a WebGL tutorial site, not actually for VSCode but I'm hoping the solution is the same.

Raffarty answered 26/9, 2016 at 3:46 Comment(1)
JSDoc works in Monaco since version 0.90. See https://mcmap.net/q/505887/-provide-type-hints-to-monaco-editor.Metempsychosis
R
47

Update: As of 0.9.0 of Monaco these type annotations now work


Is see that JSDoc style type annotations work in VSCode though they don't appear to work in Monaco.

var loc = window.location;

/** @type {WebGLRenderingContext} */
var gl = context1.getContext("webgl");    

/** @type {CanvasRenderingContext2D} */
var ctx = context2.getContext("2d"); 

enter image description here

enter image description here

Raffarty answered 26/9, 2016 at 3:58 Comment(3)
This technique doesn't appear to work for imported types. e.g., var Foo = require("foo"); /** @type {Foo} */ var foo; In this example, Foo is a type that VSCode recognizes and has full intellisense on. If instead you do var Foo = require("foo"); var foo = new Foo(); everything works. Any pro-tips to make this work correctly with imported types?Carolynecarolynn
@MicahZoltu I had a similar issue annotating types from the Express library. I found how to make this work and posted an additional answer on this question. https://mcmap.net/q/499306/-adding-javascript-type-hints-for-vscode-monaco-intellisenceEslinger
Important note: it only works if you use /** */ comments. /* */ and // comments have no effect annoyingly.Ruhl
E
6

As Micah pointed out in a comment on the accepted answer, there can still be issues with external modules. Simply doing a require of the module will already enable jsdoc type annotations to work if the library defines a global object from which you can reference the types. Otherwise, you can mimic this by importing everything and mapping it to your own name.

import * as Foo from 'foo'

/** @type {Foo.Foo} */
var foo;

https://github.com/Microsoft/TypeScript/issues/8237#issuecomment-213047062

Eslinger answered 12/2, 2018 at 17:37 Comment(1)
It also works with function parameters const foo = (/** @type {Foo.Bar} */bar) => {...};Riddick
D
1

The question lack one important detail: how were contextN variables instantiated?.

It seems they are probably of any type (instead of HTMLCanvasElement), so neither their methods nor properties have correct signature.

Chosen answer is indeed correct that we can typecast the final produced variables ("leaves"), but having correctly typecast the source ("root") objects would provide correct signatures to derived variables, so the Language Sever would be able to provide them to us, saving us that extra explicit "leaves" typing necessity.

The only what we should do here is instantiate the variable so it ends up with HTMLCanvasElement type signature.

const c1 = document.getElementsByTagName('canvas')[0];
const c2 = document.createElement('canvas');
const c3 = /** @type {HTMLCanvasElement} */(document.querySelector('.foo'));

All three ways should produce desired type, the last one using explicit typing, similar to the "leaf" typing from the other answer. N.B. such explicit type cast in JSDoc has to be on the right assignment side and have parentheses var x = /** @type {foo} */(something);

Example it works in current LS; it might probably not have worked back when the question was asked (?): Playground, code and probe outputs from the playground:

const c1 = document.getElementsByTagName('canvas')[0];
//    ^? const c1: HTMLCanvasElement
const c2 = document.createElement('canvas');
//    ^? const c2: HTMLCanvasElement
const c3 = /** @type {HTMLCanvasElement} */(document.querySelector('.foo'));
//    ^? const c3: HTMLCanvasElement
const twoD = c1.getContext('2d');
//    ^? const twoD: CanvasRenderingContext2D
const threeD = c2.getContext('webgl');
//    ^? const threeD: WebGLRenderingContext
const br = c3.getContext('bitmaprenderer');
//    ^? const br: ImageBitmapRenderingContext

Screenshot:

Dosia answered 20/5, 2024 at 9:45 Comment(0)
D
-2

If you are willing to use Typescript, you can do this:

var gl : WebGLRenderingContext;

enter image description here

Dedra answered 11/1, 2017 at 4:25 Comment(3)
Thanks! Unfortunately I'm using it on a site that's teaching stuff with JS not TS 😭Raffarty
@Raffarty Well typescript is pretty similar to javascript. much of it is very similar to javascript, very easy to convert overBeaulahbeaulieu
@Beaulahbeaulieu Nope, it's no easy: once that you are in TypeScript everything is "typed", and you must do follow with that.Sheepshanks

© 2022 - 2025 — McMap. All rights reserved.