How can I use Object.assign(this,{foo,bar}) and have VSCode Intellisense autocomplete this.foo?

Viewed 535

I have a class that looks something like this:

class MyClass {
  constructor({foo,bar}) {
    Object.assign(this,{foo,bar})
  }
}

I want to make it so when I type this.f VSCode autocompletes this.foo. I know if I do this

class MyClass {
  constructor({foo,bar}) {
    this.foo = foo
    this.bar = bar
  }
}

it will give me what I want, but I would prefer to use Object.assign(). I'm having trouble figuring out the right JSDoc or whatever to use. Adding @prop {String} foo or something at the top of the class doesn't do anything.

2 Answers

As of VS Code 1.18, we don't support intellisense for Object.assign(this). This bug is being tracked here

Since VSC uses TypeScript under the hood to provide IntelliSense, there is a workaround with some trade-offs: using @this JSDoc tag if you don't mind some duplication.

You have to define the type as an intersection of the constructor type and the assigned object type for the constructor and/or any field defined on the class you want additional this IntelliSense for:

/**
 * @typedef {{
 *   foo: string;
 *   bar: string;
 * }} MyAdditions
 */

class MyClass {
    /**
     * @param {MyAdditions} options
     * @this {MyClass & MyAdditions}
     */
    constructor({ foo, bar }) {
        Object.assign(this, { foo, bar });

        // all ok
        this.foo;
        this.bar;
    }

    /**
     * @this {MyClass & MyAdditions}
     */
    doSomethingWithMyAdditions() {
        const { foo, bar } = this; // all ok too
        console.log(`${foo}${bar}`);
    }
}

The caveat, however, is referring to the properties from class instances. Unfortunately, overriding this type of a constructor does not change its return type (given the example above, the return type is still MyClass), so this is still illegal:

const c = new MyClass({ foo: "bar", bar: "foo" });
c.foo; //Property 'foo' does not exist on type 'MyClass'
c.bar; //Property 'bar' does not exist on type 'MyClass'

A cookie smart enough to try to add a @returns tag to the constructor will be greeted with a TS1903 error "Type annotation cannot appear on a constructor declaration". Using @type annotation (i.e. @type {new () => MyClass & MyAdditions}) as an alternative simply results in a no-op.

That said, nothing stops you from casting an instance type (after all, instantiation should not happen too often) with the @type annotation (note the parenthesis around the new):

const d = /** @type {MyClass & MyAdditions} */ (new MyClass({ foo: "bar", bar: "foo" }));
d.foo; // ok now
d.bar; // ok now

Playground

In any case, this is only a workaround, and the original issue is still in the backlog.

Related