Repository: TypeScript
Issue: Allow `this` parameter in property accessors (getter/setter)
Labels: Suggestion,Awaiting More Feedback

# Suggestion

Allow the `this` parameter in property accessors:

```
const extension = {
    get bar(this:ObjectWithFoo):string {
        return this.foo;
    }
}
```

## 🔍 Search Terms

getter setter accessor this argument parameter

## ✅ Viability Checklist

My suggestion meets these guidelines:

* [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code  
    _As with functions, the `this` parameter for accessors is optional and would default to the home object._

* [x] This wouldn't change the runtime behavior of existing JavaScript code  
    _As with most of TypeScript, this proposal has no effect on emitted JavaScript._

* [x] This could be implemented without emitting different JS based on the types of the expressions  
    _As demonstrated below, this proposal technically already emits functioning JavaScript. This proposal only affects type checking (as with functions)._

* [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)  
    _This proposal only affects type checking during compilation._

* [x] This feature would agree with the rest of [TypeScript's Design Goals](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals).  
    _While this feature agrees with all of the design goals, it fits most neatly under goals 5 and 9: "Produce a language that is composable and easy to reason about." and "Use a consistent, fully erasable, structural type system."_



## ⭐ Suggestion

It is currently possible to assign a `this` parameter to a method to ensure that it is used correctly. For example:

```typescript
interface ObjectWithFoo {
    foo: "foo";
};

const extensions = {
    baz(this:ObjectWithFoo):string {
        return this.foo + "baz";
    },
};

const target:ObjectWithFoo = {
    foo: "foo",
};

const extendedTarget = Object.assign(target, extensions);

extendedTarget.baz(); // "foobaz"
```

This capability is enormously useful when defining objects that will be used to extend other types.

However, there is a glaring missing feature. While it is possible to add property accessors to the extension object, they do not have access to the `this` reference, even though the getter and setter accessors are fundamentally just the `get` and `set` functions on the property descriptor. This is severely limiting, even though it is fully supported in JavaScript.

```typescript
const extensions = {
    // The following accessor currently produces:
    //     Error: 'get' and 'set' accessors cannot declare 'this' parameters.(2784)
    get bar(this:ObjectWithFoo):string {
        return this.foo;
    },
    baz(this:ObjectWithFoo):string {
        return this.foo + "baz";
    },
};
```

## 📃 Motivating Example

Consider the following simple extension framework code. It uses `getOwnPropertyDescriptor` and `defineProperty`, which preserves the original accessor functions (the getter/setter). JavaScript evaluates the `this` references in the accessors in the context of the target object, allowing any property getter/setter logic to behave as if the properties were defined on the target from the beginning.

```typescript
function extend<T, E>(target:T, extension:E): T & E {
    // Please note that this function is offered as a reasonable stand in for an extension framework.
    // It is not, on its own, a complete solution that handles all edge-cases.
    for (const extensionName in extensions) {
        const extensionDescriptor = Object.getOwnPropertyDescriptor( extensions, extensionName );
        if (!extensionDescriptor) throw Error("Missing property descriptor for: " + extensionName);
        Object.defineProperty(target, extensionName, extensionDescriptor);
    }
    return target as T & E;
}
```

TypeScript's support for `this` on property accessors would allow extension framework authors to express this powerful feature of JavaScript and ensure correctness in their code.

```typescript
interface ObjectWithFoo {
    foo: "foo";
};

const extensions = {
    // Proposed:
    get bar(this:ObjectWithFoo):string {
        return this.foo;
    },
    baz(this:ObjectWithFoo):string {
        return this.foo + "baz";
    },
};

const target:ObjectWithFoo = {
    foo: "foo",
};

const extendedTarget = extend(target, extensions);

extendedTarget.bar; // "foo"
```

In addition to the convenience of accessing the intended `this` reference, TypeScript could use the same kind of type safety for properties that it currently offers for functions.

```typescript
// Current:
extensions.baz(); // The 'this' context of type '...' is not assignable to method's 'this' of type '...'

// Proposed:
extensions.bar; // The 'this' context of type '...' is not assignable to accessor's 'this' of type '...'
```

Checking the intended `this` reference for property accessors could be used to help prevent common mistakes, such as the following:

```typescript
Object.assign(target, extensions);
```

Here, the engineer intended to attach the `bar` property accessor functions but accidentally triggered the evaluation of the property value instead. Instead of assigning a dynamic getter, the target received a new property with a simple, fixed value (or a potential runtime error).

A [playground link](https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABAUwB5WWAJgHgCoA0iAogHwAUUAhgE4DmyUAXISupgM7xhPECUTRHkQAyEogDeAKESzEAenmIAcnAyIoACypQNmmB0ShIsBIgC2VANbJDVI8gDuiKhw4hzAB1NhDwODR6yIgc1sFwwCEwXgA2MBAwUACeAHSIAGIBbFSxyESJiFhwtohgai4QEHDguv6BAMpJ5gBGcDEcKTJyikL6hlUgMViIzcE0yNAwwDDIw46JmiOMGIFYyNNgidx+WVrBxBpJnuGB5gHBVbHxOrOIMXB08UZZWAZVAG7INDBgdIieNDgxxosFsnTkz0C5Cqvl0aAwvm4yhywR+bARXAQHD4ki6ENkMI4cPYiIQABFbBBvt4sgBeRAAeWaACsJlAUgwoAzHGAAAqA4HJCkcKkwGk0cjozjbIjw6UIZHmYJ8ADcePxU0Q5AAhHLSWBhaLxTitIDnMQaICJQAiACyBi4v3+Aq+yUKlOpUCydUE1sQAGopfrFchVeqIUzWdAUmsNsh+UDXUlKLRObKSZiwCH0xjuIbPQEwxCAL7q8ZQEA0JDUeiMFyGYRiYhq0tSH4rYBUCDBSNsgDqC0ycFxEP8cF9Y+tLbVUkJxNzWMQ9OkEM5I1olD6TF70AHWiHAiJ3ydK-xsnLlerfRSY7VJYI6uaVAAXpuDNuWf3B3A4DjT2eLyrPQDBvH8A0Qa0n2fKd1WLB9ixnHo9UzDooPIVUFCUPBNGCAByLQDFwxAYQwdBEAiQ5jkQXCJEQFJ6MQYsiIMUpylcLg6DAKhmhiYIvQsRhNDgLBcMMfC+iIijkio3D6JSWSpCQjNthSJ8aBVTCVGHS5PBgXiAFpYCVFBLQCKRZyxXQa05D8oygPdNCHJcRzkMcJx-a14JnOcgzWLA8FTOt6T1LAU1rKAc3lXwwws3w2mQFJ7jocgQtmALwtU2gMJ6a1J1ijh4sSh4UpJPz0s5TLXz4bKlFyn8oOtIA) which demonstrates the proposed code (and actually runs, despite the TS error!)

## 💻 Use Cases

The use cases for the `this` parameter in property accessors includes all the use cases for the `this` parameter in functions, including:

* Extension frameworks
* Mixins
* Class and property decorators

...and any other code where a property accessor defined on one object will ultimately be used by another.