Skip to content

Commit f72193e

Browse files
committed
Report a semantic error for an arrow function with a "this" parameter.
Fixes #9744.
1 parent 93722c8 commit f72193e

8 files changed

+170
-118
lines changed

src/compiler/checker.ts

+3
Original file line numberDiff line numberDiff line change
@@ -21857,6 +21857,9 @@ namespace ts {
2185721857
if (func.kind === SyntaxKind.Constructor || func.kind === SyntaxKind.ConstructSignature || func.kind === SyntaxKind.ConstructorType) {
2185821858
error(node, Diagnostics.A_constructor_cannot_have_a_this_parameter);
2185921859
}
21860+
if (func.kind === SyntaxKind.ArrowFunction) {
21861+
error(node, Diagnostics.An_arrow_function_cannot_have_a_this_parameter);
21862+
}
2186021863
}
2186121864

2186221865
// Only check rest parameter type if it's not a binding pattern. Since binding patterns are

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -2417,6 +2417,10 @@
24172417
"category": "Error",
24182418
"code": 2729
24192419
},
2420+
"An arrow function cannot have a 'this' parameter.": {
2421+
"category": "Error",
2422+
"code": 2730
2423+
},
24202424

24212425
"Import declaration '{0}' is using private name '{1}'.": {
24222426
"category": "Error",

src/compiler/parser.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -3537,8 +3537,9 @@ namespace ts {
35373537
}
35383538

35393539
// If we had "(" followed by something that's not an identifier,
3540-
// then this definitely doesn't look like a lambda.
3541-
if (!isIdentifier()) {
3540+
// then this definitely doesn't look like a lambda. "this" is not
3541+
// valid, but we want to parse it and then give a semantic error.
3542+
if (!isIdentifier() && second !== SyntaxKind.ThisKeyword) {
35423543
return Tristate.False;
35433544
}
35443545

tests/baselines/reference/thisTypeInFunctionsNegative.errors.txt

+16-10
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,13 @@ tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(172,39): e
9393
tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(172,40): error TS1128: Declaration or statement expected.
9494
tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(172,42): error TS2693: 'number' only refers to a type, but is being used as a value here.
9595
tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(172,49): error TS1005: ';' expected.
96-
tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(175,29): error TS2304: Cannot find name 'm'.
97-
tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(175,32): error TS1005: ';' expected.
98-
tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(175,35): error TS2304: Cannot find name 'm'.
96+
tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(175,23): error TS2730: An arrow function cannot have a 'this' parameter.
97+
tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(176,16): error TS2730: An arrow function cannot have a 'this' parameter.
98+
tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(177,19): error TS2730: An arrow function cannot have a 'this' parameter.
99+
tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(178,22): error TS2730: An arrow function cannot have a 'this' parameter.
99100

100101

101-
==== tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts (64 errors) ====
102+
==== tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts (65 errors) ====
102103
class C {
103104
n: number;
104105
explicitThis(this: this, m: number): number {
@@ -430,10 +431,15 @@ tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(175,35): e
430431

431432
// can't name parameters 'this' in a lambda.
432433
c.explicitProperty = (this, m) => m + this.n;
433-
~
434-
!!! error TS2304: Cannot find name 'm'.
435-
~~
436-
!!! error TS1005: ';' expected.
437-
~
438-
!!! error TS2304: Cannot find name 'm'.
434+
~~~~
435+
!!! error TS2730: An arrow function cannot have a 'this' parameter.
436+
const f2 = <T>(this: {n: number}, m: number) => m + this.n;
437+
~~~~~~~~~~~~~~~~~
438+
!!! error TS2730: An arrow function cannot have a 'this' parameter.
439+
const f3 = async (this: {n: number}, m: number) => m + this.n;
440+
~~~~~~~~~~~~~~~~~
441+
!!! error TS2730: An arrow function cannot have a 'this' parameter.
442+
const f4 = async <T>(this: {n: number}, m: number) => m + this.n;
443+
~~~~~~~~~~~~~~~~~
444+
!!! error TS2730: An arrow function cannot have a 'this' parameter.
439445

tests/baselines/reference/thisTypeInFunctionsNegative.js

+71-101
Original file line numberDiff line numberDiff line change
@@ -174,69 +174,60 @@ function initializer(this: C = new C()): number { return this.n; }
174174

175175
// can't name parameters 'this' in a lambda.
176176
c.explicitProperty = (this, m) => m + this.n;
177+
const f2 = <T>(this: {n: number}, m: number) => m + this.n;
178+
const f3 = async (this: {n: number}, m: number) => m + this.n;
179+
const f4 = async <T>(this: {n: number}, m: number) => m + this.n;
177180

178181

179182
//// [thisTypeInFunctionsNegative.js]
180-
var __extends = (this && this.__extends) || (function () {
181-
var extendStatics = function (d, b) {
182-
extendStatics = Object.setPrototypeOf ||
183-
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
184-
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
185-
return extendStatics(d, b);
186-
}
187-
return function (d, b) {
188-
extendStatics(d, b);
189-
function __() { this.constructor = d; }
190-
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
191-
};
192-
})();
193-
var _this = this;
194-
var C = /** @class */ (function () {
195-
function C() {
196-
}
197-
C.prototype.explicitThis = function (m) {
183+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
184+
return new (P || (P = Promise))(function (resolve, reject) {
185+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
186+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
187+
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
188+
step((generator = generator.apply(thisArg, _arguments || [])).next());
189+
});
190+
};
191+
class C {
192+
explicitThis(m) {
198193
return this.n + m;
199-
};
200-
C.prototype.implicitThis = function (m) {
194+
}
195+
implicitThis(m) {
201196
return this.n + m;
202-
};
203-
C.prototype.explicitC = function (m) {
197+
}
198+
explicitC(m) {
204199
return this.n + m;
205-
};
206-
C.prototype.explicitProperty = function (m) {
200+
}
201+
explicitProperty(m) {
207202
return this.n + m;
208-
};
209-
C.prototype.explicitVoid = function (m) {
203+
}
204+
explicitVoid(m) {
210205
return this.n + m; // 'n' doesn't exist on type 'void'.
211-
};
212-
return C;
213-
}());
214-
var D = /** @class */ (function () {
215-
function D() {
216206
}
217-
D.prototype.explicitThis = function (m) {
207+
}
208+
class D {
209+
explicitThis(m) {
218210
return this.x + m;
219-
};
220-
D.prototype.explicitD = function (m) {
211+
}
212+
explicitD(m) {
221213
return this.x + m;
222-
};
223-
return D;
224-
}());
225-
var impl = {
214+
}
215+
}
216+
let impl = {
226217
a: 12,
227-
explicitVoid1: function () {
218+
explicitVoid1() {
228219
return this.a; // error, no 'a' in 'void'
229220
},
230-
explicitVoid2: function () { return _this.a; },
231-
explicitStructural: function () { return 12; },
232-
explicitInterface: function () { return 12; },
233-
explicitThis: function () {
221+
explicitVoid2: () => this.a,
222+
explicitStructural: () => 12,
223+
explicitInterface: () => 12,
224+
explicitThis() {
234225
return this.a;
235-
}
226+
},
236227
};
237-
var implExplicitStructural = impl.explicitStructural;
228+
let implExplicitStructural = impl.explicitStructural;
238229
implExplicitStructural(); // error, no 'a' in 'void'
239-
var implExplicitInterface = impl.explicitInterface;
230+
let implExplicitInterface = impl.explicitInterface;
240231
implExplicitInterface(); // error, no 'a' in 'void'
241232
function explicitStructural(x) {
242233
return x + this.y;
@@ -247,15 +238,15 @@ function propertyName(x) {
247238
function voidThisSpecified(x) {
248239
return x + this.notSpecified;
249240
}
250-
var ok = { y: 12, explicitStructural: explicitStructural };
251-
var wrongPropertyType = { y: 'foo', explicitStructural: explicitStructural };
252-
var wrongPropertyName = { wrongName: 12, explicitStructural: explicitStructural };
241+
let ok = { y: 12, explicitStructural };
242+
let wrongPropertyType = { y: 'foo', explicitStructural };
243+
let wrongPropertyName = { wrongName: 12, explicitStructural };
253244
ok.f(); // not enough arguments
254245
ok.f('wrong type');
255246
ok.f(13, 'too many arguments');
256247
wrongPropertyType.f(13);
257248
wrongPropertyName.f(13);
258-
var c = new C();
249+
let c = new C();
259250
c.explicitC(); // not enough arguments
260251
c.explicitC('wrong type');
261252
c.explicitC(13, 'too many arguments');
@@ -269,8 +260,8 @@ c.explicitProperty(); // not enough arguments
269260
c.explicitProperty('wrong type 3');
270261
c.explicitProperty(15, 'too many arguments 3');
271262
// oops, this triggers contextual typing, which needs to be updated to understand that =>'s `this` is void.
272-
var specifiedToVoid = explicitStructural;
273-
var reconstructed = {
263+
let specifiedToVoid = explicitStructural;
264+
let reconstructed = {
274265
n: 12,
275266
explicitThis: c.explicitThis,
276267
explicitC: c.explicitC,
@@ -279,8 +270,8 @@ var reconstructed = {
279270
};
280271
;
281272
// lambdas have this: void for assignability purposes (and this unbound (free) for body checking)
282-
var d = new D();
283-
var explicitXProperty;
273+
let d = new D();
274+
let explicitXProperty;
284275
// from differing object types
285276
c.explicitC = function (m) { return this.x + m; };
286277
c.explicitProperty = explicitXProperty;
@@ -293,64 +284,41 @@ c.explicitThis = d.explicitThis;
293284
c.explicitVoid = d.explicitD;
294285
c.explicitVoid = d.explicitThis;
295286
/// class-based polymorphic assignability (with inheritance!) ///
296-
var Base1 = /** @class */ (function () {
297-
function Base1() {
298-
}
299-
Base1.prototype.polymorphic = function () { return this.x; };
300-
Base1.prototype.explicit = function () { return this.x; };
301-
Base1.explicitStatic = function () { return this.x; };
302-
return Base1;
303-
}());
304-
var Derived1 = /** @class */ (function (_super) {
305-
__extends(Derived1, _super);
306-
function Derived1() {
307-
return _super !== null && _super.apply(this, arguments) || this;
308-
}
309-
return Derived1;
310-
}(Base1));
311-
var Base2 = /** @class */ (function () {
312-
function Base2() {
313-
}
314-
Base2.prototype.polymorphic = function () { return this.y; };
315-
Base2.prototype.explicit = function () { return this.x; };
316-
return Base2;
317-
}());
318-
var Derived2 = /** @class */ (function (_super) {
319-
__extends(Derived2, _super);
320-
function Derived2() {
321-
return _super !== null && _super.apply(this, arguments) || this;
322-
}
323-
return Derived2;
324-
}(Base2));
325-
var b1 = new Base1();
326-
var d1 = new Derived1();
327-
var b2 = new Base2();
328-
var d2 = new Derived2();
287+
class Base1 {
288+
polymorphic() { return this.x; }
289+
explicit() { return this.x; }
290+
static explicitStatic() { return this.x; }
291+
}
292+
class Derived1 extends Base1 {
293+
}
294+
class Base2 {
295+
polymorphic() { return this.y; }
296+
explicit() { return this.x; }
297+
}
298+
class Derived2 extends Base2 {
299+
}
300+
let b1 = new Base1();
301+
let d1 = new Derived1();
302+
let b2 = new Base2();
303+
let d2 = new Derived2();
329304
b1.polymorphic = b2.polymorphic; // error, 'this.y' not in Base1: { x }
330305
b1.explicit = b2.polymorphic; // error, 'y' not in Base1: { x }
331306
d1.explicit = b2.polymorphic; // error, 'y' not in Base1: { x }
332307
////// use this-type for construction with new ////
333308
function VoidThis() {
334309
}
335-
var voidThis = new VoidThis();
310+
let voidThis = new VoidThis();
336311
///// syntax-ish errors /////
337-
var ThisConstructor = /** @class */ (function () {
338-
function ThisConstructor(n) {
312+
class ThisConstructor {
313+
constructor(n) {
339314
this.n = n;
340315
}
341-
return ThisConstructor;
342-
}());
316+
}
343317
var thisConstructorType;
344318
function notFirst(a) { return this.n; }
345319
///// parse errors /////
346320
function modifiers() { return this.n; }
347-
function restParam() {
348-
var = [];
349-
for (var _i = 0; _i < arguments.length; _i++) {
350-
[_i] = arguments[_i];
351-
}
352-
return this.n;
353-
}
321+
function restParam(...) { return this.n; }
354322
function optional() { return this.n; }
355323
function decorated() { return this.n; }
356324
function initializer(, C) { }
@@ -360,5 +328,7 @@ number;
360328
return this.n;
361329
}
362330
// can't name parameters 'this' in a lambda.
363-
c.explicitProperty = (this, m);
364-
m + this.n;
331+
c.explicitProperty = (m) => m + this.n;
332+
const f2 = (m) => m + this.n;
333+
const f3 = (m) => __awaiter(this, void 0, void 0, function* () { return m + this.n; });
334+
const f4 = (m) => __awaiter(this, void 0, void 0, function* () { return m + this.n; });

tests/baselines/reference/thisTypeInFunctionsNegative.symbols

+26
Original file line numberDiff line numberDiff line change
@@ -661,4 +661,30 @@ c.explicitProperty = (this, m) => m + this.n;
661661
>c.explicitProperty : Symbol(C.explicitProperty, Decl(thisTypeInFunctionsNegative.ts, 10, 5))
662662
>c : Symbol(c, Decl(thisTypeInFunctionsNegative.ts, 70, 3))
663663
>explicitProperty : Symbol(C.explicitProperty, Decl(thisTypeInFunctionsNegative.ts, 10, 5))
664+
>this : Symbol(this, Decl(thisTypeInFunctionsNegative.ts, 174, 22))
665+
>m : Symbol(m, Decl(thisTypeInFunctionsNegative.ts, 174, 27))
666+
>m : Symbol(m, Decl(thisTypeInFunctionsNegative.ts, 174, 27))
667+
668+
const f2 = <T>(this: {n: number}, m: number) => m + this.n;
669+
>f2 : Symbol(f2, Decl(thisTypeInFunctionsNegative.ts, 175, 5))
670+
>T : Symbol(T, Decl(thisTypeInFunctionsNegative.ts, 175, 12))
671+
>this : Symbol(this, Decl(thisTypeInFunctionsNegative.ts, 175, 15))
672+
>n : Symbol(n, Decl(thisTypeInFunctionsNegative.ts, 175, 22))
673+
>m : Symbol(m, Decl(thisTypeInFunctionsNegative.ts, 175, 33))
674+
>m : Symbol(m, Decl(thisTypeInFunctionsNegative.ts, 175, 33))
675+
676+
const f3 = async (this: {n: number}, m: number) => m + this.n;
677+
>f3 : Symbol(f3, Decl(thisTypeInFunctionsNegative.ts, 176, 5))
678+
>this : Symbol(this, Decl(thisTypeInFunctionsNegative.ts, 176, 18))
679+
>n : Symbol(n, Decl(thisTypeInFunctionsNegative.ts, 176, 25))
680+
>m : Symbol(m, Decl(thisTypeInFunctionsNegative.ts, 176, 36))
681+
>m : Symbol(m, Decl(thisTypeInFunctionsNegative.ts, 176, 36))
682+
683+
const f4 = async <T>(this: {n: number}, m: number) => m + this.n;
684+
>f4 : Symbol(f4, Decl(thisTypeInFunctionsNegative.ts, 177, 5))
685+
>T : Symbol(T, Decl(thisTypeInFunctionsNegative.ts, 177, 18))
686+
>this : Symbol(this, Decl(thisTypeInFunctionsNegative.ts, 177, 21))
687+
>n : Symbol(n, Decl(thisTypeInFunctionsNegative.ts, 177, 28))
688+
>m : Symbol(m, Decl(thisTypeInFunctionsNegative.ts, 177, 39))
689+
>m : Symbol(m, Decl(thisTypeInFunctionsNegative.ts, 177, 39))
664690

0 commit comments

Comments
 (0)