Report a semantic error for an arrow function with a "this" parameter.

Fixes #9744.
This commit is contained in:
Matt McCutchen
2018-07-23 10:42:56 -04:00
parent 93722c8942
commit f72193eedc
8 changed files with 171 additions and 119 deletions

View File

@@ -21857,6 +21857,9 @@ namespace ts {
if (func.kind === SyntaxKind.Constructor || func.kind === SyntaxKind.ConstructSignature || func.kind === SyntaxKind.ConstructorType) {
error(node, Diagnostics.A_constructor_cannot_have_a_this_parameter);
}
if (func.kind === SyntaxKind.ArrowFunction) {
error(node, Diagnostics.An_arrow_function_cannot_have_a_this_parameter);
}
}
// Only check rest parameter type if it's not a binding pattern. Since binding patterns are

View File

@@ -2417,6 +2417,10 @@
"category": "Error",
"code": 2729
},
"An arrow function cannot have a 'this' parameter.": {
"category": "Error",
"code": 2730
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",

View File

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

View File

@@ -93,12 +93,13 @@ tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(172,39): e
tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(172,40): error TS1128: Declaration or statement expected.
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.
tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(172,49): error TS1005: ';' expected.
tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(175,29): error TS2304: Cannot find name 'm'.
tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(175,32): error TS1005: ';' expected.
tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(175,35): error TS2304: Cannot find name 'm'.
tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(175,23): error TS2730: An arrow function cannot have a 'this' parameter.
tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(176,16): error TS2730: An arrow function cannot have a 'this' parameter.
tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(177,19): error TS2730: An arrow function cannot have a 'this' parameter.
tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(178,22): error TS2730: An arrow function cannot have a 'this' parameter.
==== tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts (64 errors) ====
==== tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts (65 errors) ====
class C {
n: number;
explicitThis(this: this, m: number): number {
@@ -430,10 +431,15 @@ tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(175,35): e
// can't name parameters 'this' in a lambda.
c.explicitProperty = (this, m) => m + this.n;
~
!!! error TS2304: Cannot find name 'm'.
~~
!!! error TS1005: ';' expected.
~
!!! error TS2304: Cannot find name 'm'.
~~~~
!!! error TS2730: An arrow function cannot have a 'this' parameter.
const f2 = <T>(this: {n: number}, m: number) => m + this.n;
~~~~~~~~~~~~~~~~~
!!! error TS2730: An arrow function cannot have a 'this' parameter.
const f3 = async (this: {n: number}, m: number) => m + this.n;
~~~~~~~~~~~~~~~~~
!!! error TS2730: An arrow function cannot have a 'this' parameter.
const f4 = async <T>(this: {n: number}, m: number) => m + this.n;
~~~~~~~~~~~~~~~~~
!!! error TS2730: An arrow function cannot have a 'this' parameter.

View File

@@ -174,69 +174,60 @@ function initializer(this: C = new C()): number { return this.n; }
// can't name parameters 'this' in a lambda.
c.explicitProperty = (this, m) => m + this.n;
const f2 = <T>(this: {n: number}, m: number) => m + this.n;
const f3 = async (this: {n: number}, m: number) => m + this.n;
const f4 = async <T>(this: {n: number}, m: number) => m + this.n;
//// [thisTypeInFunctionsNegative.js]
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
class C {
explicitThis(m) {
return this.n + m;
}
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var _this = this;
var C = /** @class */ (function () {
function C() {
implicitThis(m) {
return this.n + m;
}
C.prototype.explicitThis = function (m) {
explicitC(m) {
return this.n + m;
};
C.prototype.implicitThis = function (m) {
}
explicitProperty(m) {
return this.n + m;
};
C.prototype.explicitC = function (m) {
return this.n + m;
};
C.prototype.explicitProperty = function (m) {
return this.n + m;
};
C.prototype.explicitVoid = function (m) {
}
explicitVoid(m) {
return this.n + m; // 'n' doesn't exist on type 'void'.
};
return C;
}());
var D = /** @class */ (function () {
function D() {
}
D.prototype.explicitThis = function (m) {
}
class D {
explicitThis(m) {
return this.x + m;
};
D.prototype.explicitD = function (m) {
}
explicitD(m) {
return this.x + m;
};
return D;
}());
var impl = {
}
}
let impl = {
a: 12,
explicitVoid1: function () {
explicitVoid1() {
return this.a; // error, no 'a' in 'void'
},
explicitVoid2: function () { return _this.a; },
explicitStructural: function () { return 12; },
explicitInterface: function () { return 12; },
explicitThis: function () {
explicitVoid2: () => this.a,
explicitStructural: () => 12,
explicitInterface: () => 12,
explicitThis() {
return this.a;
}
},
};
var implExplicitStructural = impl.explicitStructural;
let implExplicitStructural = impl.explicitStructural;
implExplicitStructural(); // error, no 'a' in 'void'
var implExplicitInterface = impl.explicitInterface;
let implExplicitInterface = impl.explicitInterface;
implExplicitInterface(); // error, no 'a' in 'void'
function explicitStructural(x) {
return x + this.y;
@@ -247,15 +238,15 @@ function propertyName(x) {
function voidThisSpecified(x) {
return x + this.notSpecified;
}
var ok = { y: 12, explicitStructural: explicitStructural };
var wrongPropertyType = { y: 'foo', explicitStructural: explicitStructural };
var wrongPropertyName = { wrongName: 12, explicitStructural: explicitStructural };
let ok = { y: 12, explicitStructural };
let wrongPropertyType = { y: 'foo', explicitStructural };
let wrongPropertyName = { wrongName: 12, explicitStructural };
ok.f(); // not enough arguments
ok.f('wrong type');
ok.f(13, 'too many arguments');
wrongPropertyType.f(13);
wrongPropertyName.f(13);
var c = new C();
let c = new C();
c.explicitC(); // not enough arguments
c.explicitC('wrong type');
c.explicitC(13, 'too many arguments');
@@ -269,8 +260,8 @@ c.explicitProperty(); // not enough arguments
c.explicitProperty('wrong type 3');
c.explicitProperty(15, 'too many arguments 3');
// oops, this triggers contextual typing, which needs to be updated to understand that =>'s `this` is void.
var specifiedToVoid = explicitStructural;
var reconstructed = {
let specifiedToVoid = explicitStructural;
let reconstructed = {
n: 12,
explicitThis: c.explicitThis,
explicitC: c.explicitC,
@@ -279,8 +270,8 @@ var reconstructed = {
};
;
// lambdas have this: void for assignability purposes (and this unbound (free) for body checking)
var d = new D();
var explicitXProperty;
let d = new D();
let explicitXProperty;
// from differing object types
c.explicitC = function (m) { return this.x + m; };
c.explicitProperty = explicitXProperty;
@@ -293,64 +284,41 @@ c.explicitThis = d.explicitThis;
c.explicitVoid = d.explicitD;
c.explicitVoid = d.explicitThis;
/// class-based polymorphic assignability (with inheritance!) ///
var Base1 = /** @class */ (function () {
function Base1() {
}
Base1.prototype.polymorphic = function () { return this.x; };
Base1.prototype.explicit = function () { return this.x; };
Base1.explicitStatic = function () { return this.x; };
return Base1;
}());
var Derived1 = /** @class */ (function (_super) {
__extends(Derived1, _super);
function Derived1() {
return _super !== null && _super.apply(this, arguments) || this;
}
return Derived1;
}(Base1));
var Base2 = /** @class */ (function () {
function Base2() {
}
Base2.prototype.polymorphic = function () { return this.y; };
Base2.prototype.explicit = function () { return this.x; };
return Base2;
}());
var Derived2 = /** @class */ (function (_super) {
__extends(Derived2, _super);
function Derived2() {
return _super !== null && _super.apply(this, arguments) || this;
}
return Derived2;
}(Base2));
var b1 = new Base1();
var d1 = new Derived1();
var b2 = new Base2();
var d2 = new Derived2();
class Base1 {
polymorphic() { return this.x; }
explicit() { return this.x; }
static explicitStatic() { return this.x; }
}
class Derived1 extends Base1 {
}
class Base2 {
polymorphic() { return this.y; }
explicit() { return this.x; }
}
class Derived2 extends Base2 {
}
let b1 = new Base1();
let d1 = new Derived1();
let b2 = new Base2();
let d2 = new Derived2();
b1.polymorphic = b2.polymorphic; // error, 'this.y' not in Base1: { x }
b1.explicit = b2.polymorphic; // error, 'y' not in Base1: { x }
d1.explicit = b2.polymorphic; // error, 'y' not in Base1: { x }
////// use this-type for construction with new ////
function VoidThis() {
}
var voidThis = new VoidThis();
let voidThis = new VoidThis();
///// syntax-ish errors /////
var ThisConstructor = /** @class */ (function () {
function ThisConstructor(n) {
class ThisConstructor {
constructor(n) {
this.n = n;
}
return ThisConstructor;
}());
}
var thisConstructorType;
function notFirst(a) { return this.n; }
///// parse errors /////
function modifiers() { return this.n; }
function restParam() {
var = [];
for (var _i = 0; _i < arguments.length; _i++) {
[_i] = arguments[_i];
}
return this.n;
}
function restParam(...) { return this.n; }
function optional() { return this.n; }
function decorated() { return this.n; }
function initializer(, C) { }
@@ -360,5 +328,7 @@ number;
return this.n;
}
// can't name parameters 'this' in a lambda.
c.explicitProperty = (this, m);
m + this.n;
c.explicitProperty = (m) => m + this.n;
const f2 = (m) => m + this.n;
const f3 = (m) => __awaiter(this, void 0, void 0, function* () { return m + this.n; });
const f4 = (m) => __awaiter(this, void 0, void 0, function* () { return m + this.n; });

View File

@@ -661,4 +661,30 @@ c.explicitProperty = (this, m) => m + this.n;
>c.explicitProperty : Symbol(C.explicitProperty, Decl(thisTypeInFunctionsNegative.ts, 10, 5))
>c : Symbol(c, Decl(thisTypeInFunctionsNegative.ts, 70, 3))
>explicitProperty : Symbol(C.explicitProperty, Decl(thisTypeInFunctionsNegative.ts, 10, 5))
>this : Symbol(this, Decl(thisTypeInFunctionsNegative.ts, 174, 22))
>m : Symbol(m, Decl(thisTypeInFunctionsNegative.ts, 174, 27))
>m : Symbol(m, Decl(thisTypeInFunctionsNegative.ts, 174, 27))
const f2 = <T>(this: {n: number}, m: number) => m + this.n;
>f2 : Symbol(f2, Decl(thisTypeInFunctionsNegative.ts, 175, 5))
>T : Symbol(T, Decl(thisTypeInFunctionsNegative.ts, 175, 12))
>this : Symbol(this, Decl(thisTypeInFunctionsNegative.ts, 175, 15))
>n : Symbol(n, Decl(thisTypeInFunctionsNegative.ts, 175, 22))
>m : Symbol(m, Decl(thisTypeInFunctionsNegative.ts, 175, 33))
>m : Symbol(m, Decl(thisTypeInFunctionsNegative.ts, 175, 33))
const f3 = async (this: {n: number}, m: number) => m + this.n;
>f3 : Symbol(f3, Decl(thisTypeInFunctionsNegative.ts, 176, 5))
>this : Symbol(this, Decl(thisTypeInFunctionsNegative.ts, 176, 18))
>n : Symbol(n, Decl(thisTypeInFunctionsNegative.ts, 176, 25))
>m : Symbol(m, Decl(thisTypeInFunctionsNegative.ts, 176, 36))
>m : Symbol(m, Decl(thisTypeInFunctionsNegative.ts, 176, 36))
const f4 = async <T>(this: {n: number}, m: number) => m + this.n;
>f4 : Symbol(f4, Decl(thisTypeInFunctionsNegative.ts, 177, 5))
>T : Symbol(T, Decl(thisTypeInFunctionsNegative.ts, 177, 18))
>this : Symbol(this, Decl(thisTypeInFunctionsNegative.ts, 177, 21))
>n : Symbol(n, Decl(thisTypeInFunctionsNegative.ts, 177, 28))
>m : Symbol(m, Decl(thisTypeInFunctionsNegative.ts, 177, 39))
>m : Symbol(m, Decl(thisTypeInFunctionsNegative.ts, 177, 39))

View File

@@ -777,16 +777,53 @@ function initializer(this: C = new C()): number { return this.n; }
// can't name parameters 'this' in a lambda.
c.explicitProperty = (this, m) => m + this.n;
>c.explicitProperty = (this, m) : any
>c.explicitProperty = (this, m) => m + this.n : (this: { n: number; }, m: number) => any
>c.explicitProperty : (this: { n: number; }, m: number) => number
>c : C
>explicitProperty : (this: { n: number; }, m: number) => number
>(this, m) : any
>this, m : any
>this : any
>m : any
>(this, m) => m + this.n : (this: { n: number; }, m: number) => any
>this : { n: number; }
>m : number
>m + this.n : any
>m : any
>m : number
>this.n : any
>this : any
>n : any
const f2 = <T>(this: {n: number}, m: number) => m + this.n;
>f2 : <T>(this: { n: number; }, m: number) => any
><T>(this: {n: number}, m: number) => m + this.n : <T>(this: { n: number; }, m: number) => any
>T : T
>this : { n: number; }
>n : number
>m : number
>m + this.n : any
>m : number
>this.n : any
>this : any
>n : any
const f3 = async (this: {n: number}, m: number) => m + this.n;
>f3 : (this: { n: number; }, m: number) => Promise<any>
>async (this: {n: number}, m: number) => m + this.n : (this: { n: number; }, m: number) => Promise<any>
>this : { n: number; }
>n : number
>m : number
>m + this.n : any
>m : number
>this.n : any
>this : any
>n : any
const f4 = async <T>(this: {n: number}, m: number) => m + this.n;
>f4 : <T>(this: { n: number; }, m: number) => Promise<any>
>async <T>(this: {n: number}, m: number) => m + this.n : <T>(this: { n: number; }, m: number) => Promise<any>
>T : T
>this : { n: number; }
>n : number
>m : number
>m + this.n : any
>m : number
>this.n : any
>this : any
>n : any

View File

@@ -1,3 +1,5 @@
// @target: es6
class C {
n: number;
explicitThis(this: this, m: number): number {
@@ -173,3 +175,6 @@ function initializer(this: C = new C()): number { return this.n; }
// can't name parameters 'this' in a lambda.
c.explicitProperty = (this, m) => m + this.n;
const f2 = <T>(this: {n: number}, m: number) => m + this.n;
const f3 = async (this: {n: number}, m: number) => m + this.n;
const f4 = async <T>(this: {n: number}, m: number) => m + this.n;