TypeScript: 'get' and 'set' accessors cannot declare 'this' parameters.

This error occurs after vSCode is updated. code example: views/view.model.ts

export class View$Model {
    get closestModelView(this: View): View {
        return this.closest(x => x.isModel == true || x.isTemplate);
    }
    get closestModel(this: View): Model {
        var view = this.closestModelView;
        if (view) {
            return view.model;
        }
        return null;
    }
}

view.ts

///<reference path='views/view.model.ts'/>
export class View extends ViewParse.Plug.BaseControl{  ...  }
export interface View { 
     on(name:'click',fn:(view:View)=>void)
     ....
}
export interface View extends View$Model { }
Util.inherit(View, View$Model)

tsconfig.json

{
    "compilerOptions": {
        "outFile": "../ts/view.js",
        "declaration": true,
        "target": "es6",
        "removeComments": true,
        "sourceMap": true
    }
}

current typescript version:3.9.5 current @types/node version:14.0.14 current vscode version:1.45.1 image

the “this: View” Will report an error, prompt: ‘get’ and ‘set’ accessors cannot declare ‘this’ parameters. I know what this error message means, but it’s always been written like this. Has Typescript tweaked its rules? I don’t want to change the code. I don’t like code that is too long for a single file, and I have a lot of code to change.This pattern simulates partial classes. I want to know what to do about it, the reduced version, the special quote statement, or what? I have checked the sample question, but no one mentioned it。 This happens every time you compile, which is bad. To seek help

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 2
  • Comments: 16 (1 by maintainers)

Most upvoted comments

This change also broke something that I sometimes do with TypesScript & JS prototype - something like “extension methods” Playground

declare global {
  interface Array<T> extends MyArrayExtension<T> {}
}

@extention(Array)
export abstract class MyArrayExtension<T> {
  get myLength(this: Array<T>) {
    return this.length;
  }
}

function extention(clz: any) {
  return function (target: any) {
    // iterate 'target' methods, getters, setters - and add them to 'clz.prototype'
  }
}

A ‘get’ accessor cannot have parameters. (1054)

How should another this then be bound to a property at all?

@tadhgmister Okay, fair enough. I didn’t intend to argue, I just wasn’t sure if I got it right.

Also, one workaround I found, and which I’d call acceptable (to my taste), is to declare the static property on the heir. It feels almost like a configuration property required by the “API” of Singleton.

class MyClass extends Singleton() {
  static i: MyClass

  hello() {
    // ...
  }
}

MyClass.i.hello

@rgbui

I see the problem, but my one is different from this one。

Not it isn’t, nothing is stopping a user from doing new View$Model().closestModelView having it type check and getting a runtime error. @kobiburnley is a lot safer by labeling the class as abstract so someone would need to subclass it then instantiate which is much less likely to do accidentally, but still the point is that typescript can’t validate the this parameter on accessors in the same way as methods so letting you do it leads to a false sense of security.

I think people who purposely write ‘this’ know exactly what they’re doing.

You could make this argument for pretty much everything, if we just assume the user knows what they are doing at all times then why typescript at all? You know this isn’t the case because your next statement contradicts it:

36883 people don’t understand Typescript and JS very well, but get it wrong.

As the person who raised that ticket I do take personal offence to this, I very clearly outline my actual use case in my second comment, I was very well aware that I was using this correctly and the runtime behaviour was correct.

I’m going to simulate partial classes.Write a class as multiple files.Because the code is too long for a single file, it’s not particularly maintainable. If not, how do partial classes express themselves? @RyanCavanaugh How do you do this if you emulate partial classes, current Typescript versions? In the current version, the “get” and “Set” Accessors "simulation doesn’t seem to work.

Normally when there are definitions in your class that are implemented in a way you can’t express to typescript, you create an interface to merge with the class, in your case this would cause cyclical references so to break it I would recommend Picking the properties that you actually use, something like this:

// pick only the fields of View that this extension relies on.
export interface View$Model extends Pick<View, "closest"> {}
export abstract class View$Model { // added abstract to ensure people don't instantiate this directly by mistake.
    get closestModelView(): View {
        return this.closest(x => x.isModel == true || x.isTemplate);
    }
    get closestModel(): Model | null {
        const view = this.closestModelView;
        if (view) {
            return view.model;
        }
        return null;
    }
}

This has an added benefit that you end up documenting the dependencies between your partial class parts at the cost that intellisense won’t suggest fields that aren’t already mentioned in the Pick.

Another option that may be more applicable for @kobiburnley’s case is to just use good old fashioned this as Type if there are relatively few places it is needed:

@extension(Array)
export abstract class MyArrayExtension<T> {
    get myLength() {
        return (this as unknown as Array<T>).length;
    }
}

This has the benefit that if something does go wrong the as unknown as Type sticks out as something that is probably the root issue at the cost of being noisy.