VS Code to autocomplete JavaScript class 'this' properties automatically
react-etc.net
react-etc.net
VS code doesn't autocomplete "this" for "old-school" (function-provided "this" objects and manual prototype inheritance etc.) object-orientish JS; it doesn't compute it properly or at all for re-bound "this" (e.g. apply()/bind()).
Making autocompletion analysis work for non-straightforward non-ES6 OO javascript is a very hard (maybe impossible even without any "eval"ed code, though I'm not sure) thing to do, so this isn't a ding on VS code: any autocompletion is better than none.
That said, if the only way to get good OO-code autocompletion is to reduce the subset of the language/OO models usable to basically "what you could have in Java/C#", it might be a good opportunity to re-evaluate your choice of language. Transpilers for more easily-statically-analyzable languages do exist, after all.
I find TypeScript does a pretty good job of allowing as much static analysis as you want without preventing you from using all the features of JS that break it if you wish. In an ideal world I'd work in a fully static language that transpiles to JS but the reality is it's usually necessary to deal with legacy JS libraries. I haven't seen a fully static transpiled language deal with that gracefully, but I'd love to be wrong about that!
With regards to static-analysis capability as a means of ensuring code reliability, I don't agree. Some people (I'm not characterizing you here since I am unsure if this is your position) consider static analyzability to be equivalent to a strong type system or various safety guarantees with regards to making code's behavior verifiable and/or predictable.
If you subscribe to that belief, having an 'escape hatch' that lets you use features of a platform that violate the analyzability guarantees is a virtual guarantee of trouble. I'm put in mind of Rust's "unsafe" areas (there are many other examples of this phenomenon): not only do people routinely overuse them (out of ignorance or misplaced, incorrect, and unverified assertions of "elegance" or "performance"), but their existence at all compromises the ability to verify even small subparts of the greater program: https://news.ycombinator.com/item?id=15146330
Having analyzability/guarantees with an escape hatch is better than having none at all, but a lot of people don't understand the tradeoff there.
I feel like the programming community was quick to give up static typing when CPUs got fast enough that the performance hit was irrelevant (for many uses), and now we are starting to realize that types were a good idea for correctness too.
Scala.js is specifically designed to be very good at this. You might want to give a shot, if you haven't already ;) Usually you would stay within the type-safe Scala code, or even talk to JavaScript libraries in a type-safe way. But it will always let you do anything you could do in JavaScript (it is one of its core language design decisions). If you want to do something especially weird, the syntax to do so might be pretty awkward, but in most cases it will feel very natural.
I think the next step would be to see someone champion through TC39 the "Python model" where browsers/engines should at least parse and ignore type annotations per spec. Then at least you could pass Typescript/Flow annotated code directly to a browser, even if the browser/engines don't immediately do anything with those extra annotations.
Just interface {}, as Y, and :type annotations in JavaScript would take us very far.
On that note, how does one even submit an ecmascript proposal?
In Scala the compile time alone is a massive drag.
I believe that a strong static type system with generics is an unmitigated win, and that any time you think you may have lost fighting the compiler is more than regained by the time saved not having to track down type bugs at runtime, using the IDE to perform code completion and refactoring, etc. This effect is magnified as the project grows large.
Or Kotlin for both front/back with transpilation to JS.
It's just that dynamically-typed languages are still popular. And Javascript continually grows and is often a good choice, like for client-side applications. So it's not time wasted if we can figure out ways to improve the tooling experience for these languages.
For example, there are just other considerations beyond static-typing when choosing a language. It seems condescending to suggest that people are unaware rather than just picking different trade-offs.
So intellisense news in an IDE is certainly nothing new but it is "newish" for more functional/dynamic languages like JS. JetBrains has also done a good job with their intellisense in their IDEs for Python and Ruby, for example.
1. I mean why hasn't it existed before? Surely an editor can figure out which object you're in and auto-complete this on the basis of that.
2. If it is hard for some reason, how did they do it?
In TypeScript code ‘this’ is typed, and we can infer the type of this for classes and ES5 classes in JS. However we don’t analyze calls to ‘.call’ or ‘.apply’ to infer the type of ‘this’ in a given function.
function example() { console.log(this.v); };
Which I call twice: example.apply({ v: 'One' })
example.apply({ v: 'Two' })
How can code analysis tell you the value of `this`? class ExampleClass {
example() { console.log(this.v); }
}
Called as such: const e = new ExampleClass();
e.example.apply({ v: 'One' });
e.example.apply({ v: 'Two' }); 'this' implicitly has type 'any' because it does not have a type annotation.
That error can be fixed by specifying an explicit type annotation for this: function example(this: { v: string }) { console.log(this.v); };
However, the type checking for apply is still too weak to produce any useful errors, as even with the above type annotation, this will still compile: example.apply({ b: 3 })
The type of apply is currently: Function.apply(this: Function, thisArg: any, argArray?: any): any
It accepts basically any arguments. In future perhaps they will make this stricter, more discussion here: https://github.com/Microsoft/TypeScript/issues/2122. From what I can tell, VS Code DOESN'T solve it. It only autocompletes the "this" object under certain circumstances
(I could be wrong on this last point, since I haven't played around with it first hand)
Yes this feature is completely obvious in hindsight, so much so that I felt a little silly highlighting that we never did it before. I guess sometimes you just need someone to file a feature request to point this type of thing out
Anyways, please try it out in the current VS Code insiders builds and VS Code 1.20 once it is released next week. Lots of much more exciting stuff coming in 1.20 as well
For example, this is a pattern I see a lot:
const args = parseCliFlags({
help: {
parser: Boolean,
description: 'whether to show help',
short: 'h',
},
custom: {
parser: (v: string) => {return {hmmm: 'yes'}},
repeated: true
}
});
if (args.help) {
console.log('help screen!');
}
for (const custom of args.custom) {
console.log(custom.hmmm);
}
So we produce an args object based on the descriptors passed into parseCliFlags. Specifically, we use the parser function and the repeated field. With the above PRs, this code can be fully type checked: interface Descriptor {
readonly parser: (value: string) => any;
readonly description?: string;
readonly short?: string;
readonly repeated?: true;
}
interface Descriptors {
readonly [flag: string]: Descriptor;
}
type FlagTypes<T> = { [K in keyof T]: FlagTypeOf<T[K]> };
type ReturnTypeOf<V> = V extends (...args: any[])=>infer R ? R : never;
type FlagTypeOf<V> = V extends {repeated: true, parser: (val: string)=> infer R} ? R[] : V extends {parser: (val: string)=> infer R} ? R : never;
That code is tricky to write, but it only needs to be written once, and submitted to DefinitelyTyped. From that point, every VSCode user could benefit from these typings (they already have this part working today, with typings for your node libraries downloaded automatically from DefinitelyTyped).It doesn't complete "this." but rather when you type a member variable it inserts "this.member". ex. type "width" and it replaces it with "this.width".
I'm constantly typing "bind(this)", while the default case should be that "this" within a class points to the object.
I understand backward compatibility, but I wish there was some command I could add at the beginning of the file to make JS behave normally. Kind of like "use strict".
This[0] page says this problem can be also seen in Typescript. But TS has the arrow function as a class method feature that in Javascript is only at stage 2 in it's way to standardization, so it's easier to solve there.
[0] https://github.com/Microsoft/TypeScript/wiki/'this'-in-TypeS...
`this` is the receiver of the function, which can change depending on how the function is invoked, scope, etc.
Edit: I didn't realize this would be a contentious point! I'm not trying to advocate for any particular style of JavaScript, just making the point that places in Java where "this" can be omitted, in JavaScript it cannot.
Certain frameworks will encourage more of its use (such as with passing functions-as-props in React), but Javascript itself can support multiple paradigms, with the `this` keyword supporting only some of its messiest parts.
Java will probably always be a better language for writing Java than JavaScript is, but the Java-in-browsers story has sucked for a long time, so we’re stuck with people who want Java using JavaScript as Java.