Typescript: accessor allows `this` parameter but is not checked or enforced.

Created on 20 Feb 2020  路  3Comments  路  Source: microsoft/TypeScript

TypeScript Version: 3.7.5 and 3.8 (91ffa1c752ac26882b1426fb4c9012701c1d908e)


Search Terms:
getter, get, set, setter, accessor property, this parameter
Code
In the following, x.a and x.b() are invalid for the same reason but x.a doesn't give a type error.

interface Unimplemented {
    calculate(): number;
}

class Demo {
   get a(this: Unimplemented) {
        return this.calculate();
    }
    b(this: Unimplemented) {
        return this.calculate();
    }
}
const x = new Demo();
console.log(x.a); // no type error, fails at runtime

console.log(x.b()) // correctly gives following error:
/* The 'this' context of type 'Demo' is not assignable to method's 'this' of type 'Unimplemented'.
     Property 'calculate' is missing in type 'Demo' but required in type 'Unimplemented'.ts(2684) */

Expected behavior is one of:

  1. error to declare a this parameter on an accessor field (get or set)
  2. OR accessing x.a gives same type error as calling x.b()

Actual behavior:
this parameter is syntactically allowed on accessor but has no impact outside the function so it is never checked. Accessing x.a throws an error at runtime.

Playground Link:
Playground Link

Related Issues:
none found, It's quite possible I'm the first one to actually try something like this.

Bug Fixed good first issue help wanted

Most helpful comment

We should give an error on the this parameter like

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

or

Accessors cannot declare 'this' parameters.

I prefer the first one.

All 3 comments

For reference, what I was doing when I ran into this is that I wanted to write an abstract class with a static method that would work as a valid react component, something like this:

abstract class HookCls<P>{
    abstract useRender(props: P): React.ReactElement | null;
    public static JSX<P>(this: new()=>HookCls<P>, props: P){
        const instRef = React.useRef<HookCls<P>>();
        const inst = instRef.current ?? (instRef.current = new this());
        return inst.useRender(props);
    }
}

The constraint means that HookCls.JSX({}) is invalid because HookCls doesn't extend the concrete constructor, but a subclass like Example.JSX({}) would work, however when it's used in JSX like <Example.JSX a="test"/> means it doesn't keep the this value because it passes an unbound method to react, so I needed to replace it with a getter:

abstract class HookCls<P> {
    abstract useRender(props: P): React.ReactElement | null;
    public static get JSX<P>(this: new () => HookCls<P>) {
        return (props: P) => {
            const instRef = React.useRef<HookCls<P>>();
            const inst = instRef.current ?? (instRef.current = new this());
            return inst.useRender(props);
        };
    }
}

Right away typescript told me that generics on a getter is not valid, but it took me a while to find that it also doesn't support a this parameter correctly.

It would be amazing if typescript was capable of representing that HookCls.JSX has a different type on subclasses, (and it can be transformed based on a generic on the getter) but I recognize how much changes it would take to get something like this to be supported for such a very niche use case. So I expect typescript should just not allow declaring a this on accessors just like generics because it has no way to enforce it.

We should give an error on the this parameter like

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

or

Accessors cannot declare 'this' parameters.

I prefer the first one.

Thanks @a-tarasyuk!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jonathandturner picture jonathandturner  路  147Comments

fdecampredon picture fdecampredon  路  358Comments

disshishkov picture disshishkov  路  224Comments

nitzantomer picture nitzantomer  路  135Comments

rbuckton picture rbuckton  路  139Comments