diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 32e82d9d537..be98dc985b5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7383,6 +7383,10 @@ namespace ts { captureLexicalThis(node, container); } if (isFunctionLike(container)) { + const type = getContextuallyTypedThisType(container); + if (type) { + return type; + } const signature = getSignatureFromDeclaration(container); if (signature.thisType) { return signature.thisType; @@ -7633,6 +7637,19 @@ namespace ts { } } + function getContextuallyTypedThisType(func: FunctionLikeDeclaration): Type { + if ((isFunctionExpressionOrArrowFunction(func) || isObjectLiteralMethod(func)) && + isContextSensitive(func) && + func.kind !== SyntaxKind.ArrowFunction) { + const contextualSignature = getContextualSignature(func); + if (contextualSignature) { + return contextualSignature.thisType; + } + } + + return undefined; + } + // Return contextual type of parameter or undefined if no contextual type is available function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type { const func = parameter.parent; @@ -10396,12 +10413,6 @@ namespace ts { } function assignContextualParameterTypes(signature: Signature, context: Signature, mapper: TypeMapper) { - if (context.thisType) { - if (signature.declaration.kind !== SyntaxKind.ArrowFunction) { - // do not contextually type thisType for ArrowFunction. - signature.thisType = context.thisType; - } - } const len = signature.parameters.length - (signature.hasRestParameter ? 1 : 0); for (let i = 0; i < len; i++) { const parameter = signature.parameters[i]; diff --git a/tests/baselines/reference/thisTypeInFunctions.types b/tests/baselines/reference/thisTypeInFunctions.types index b0fc9fc3300..e1044bb3d90 100644 --- a/tests/baselines/reference/thisTypeInFunctions.types +++ b/tests/baselines/reference/thisTypeInFunctions.types @@ -143,7 +143,7 @@ function implicitThis(n: number): number { let impl: I = { >impl : I >I : I ->{ a: 12, explicitVoid2: () => this.a, // ok, this: any because it refers to some outer object (window?) explicitVoid1() { return 12; }, explicitStructural() { return this.a; }, explicitInterface() { return this.a; }, explicitThis() { return this.a; }, implicitMethod() { return this.a; }, implicitFunction: () => this.a, // ok, this: any because it refers to some outer object (window?)} : { a: number; explicitVoid2: () => any; explicitVoid1(): number; explicitStructural(this: { a: number; }): number; explicitInterface(this: I): number; explicitThis(this: I): number; implicitMethod(this: I): number; implicitFunction: () => any; } +>{ a: 12, explicitVoid2: () => this.a, // ok, this: any because it refers to some outer object (window?) explicitVoid1() { return 12; }, explicitStructural() { return this.a; }, explicitInterface() { return this.a; }, explicitThis() { return this.a; }, implicitMethod() { return this.a; }, implicitFunction: () => this.a, // ok, this: any because it refers to some outer object (window?)} : { a: number; explicitVoid2: () => any; explicitVoid1(): number; explicitStructural(): number; explicitInterface(): number; explicitThis(): number; implicitMethod(): number; implicitFunction: () => any; } a: 12, >a : number @@ -161,7 +161,7 @@ let impl: I = { >12 : number explicitStructural() { ->explicitStructural : (this: { a: number; }) => number +>explicitStructural : () => number return this.a; >this.a : number @@ -170,7 +170,7 @@ let impl: I = { }, explicitInterface() { ->explicitInterface : (this: I) => number +>explicitInterface : () => number return this.a; >this.a : number @@ -179,7 +179,7 @@ let impl: I = { }, explicitThis() { ->explicitThis : (this: I) => number +>explicitThis : () => number return this.a; >this.a : number @@ -188,7 +188,7 @@ let impl: I = { }, implicitMethod() { ->implicitMethod : (this: I) => number +>implicitMethod : () => number return this.a; >this.a : number @@ -220,21 +220,21 @@ impl.explicitVoid2 = () => 12; >12 : number impl.explicitStructural = function() { return this.a; }; ->impl.explicitStructural = function() { return this.a; } : (this: { a: number; }) => number +>impl.explicitStructural = function() { return this.a; } : () => number >impl.explicitStructural : (this: { a: number; }) => number >impl : I >explicitStructural : (this: { a: number; }) => number ->function() { return this.a; } : (this: { a: number; }) => number +>function() { return this.a; } : () => number >this.a : number >this : { a: number; } >a : number impl.explicitInterface = function() { return this.a; }; ->impl.explicitInterface = function() { return this.a; } : (this: I) => number +>impl.explicitInterface = function() { return this.a; } : () => number >impl.explicitInterface : (this: I) => number >impl : I >explicitInterface : (this: I) => number ->function() { return this.a; } : (this: I) => number +>function() { return this.a; } : () => number >this.a : number >this : I >a : number @@ -256,21 +256,21 @@ impl.explicitInterface = () => 12; >12 : number impl.explicitThis = function () { return this.a; }; ->impl.explicitThis = function () { return this.a; } : (this: I) => number +>impl.explicitThis = function () { return this.a; } : () => number >impl.explicitThis : (this: I) => number >impl : I >explicitThis : (this: I) => number ->function () { return this.a; } : (this: I) => number +>function () { return this.a; } : () => number >this.a : number >this : I >a : number impl.implicitMethod = function () { return this.a; }; ->impl.implicitMethod = function () { return this.a; } : (this: I) => number +>impl.implicitMethod = function () { return this.a; } : () => number >impl.implicitMethod : (this: I) => number >impl : I >implicitMethod : (this: I) => number ->function () { return this.a; } : (this: I) => number +>function () { return this.a; } : () => number >this.a : number >this : I >a : number @@ -719,11 +719,11 @@ c.explicitThis = function(this: C, m: number) { return this.n + m }; // this:any compatibility c.explicitC = function(m) { return this.n + m }; ->c.explicitC = function(m) { return this.n + m } : (this: C, m: number) => number +>c.explicitC = function(m) { return this.n + m } : (m: number) => number >c.explicitC : (this: C, m: number) => number >c : C >explicitC : (this: C, m: number) => number ->function(m) { return this.n + m } : (this: C, m: number) => number +>function(m) { return this.n + m } : (m: number) => number >m : number >this.n + m : number >this.n : number @@ -732,11 +732,11 @@ c.explicitC = function(m) { return this.n + m }; >m : number c.explicitProperty = function(m) { return this.n + m }; ->c.explicitProperty = function(m) { return this.n + m } : (this: { n: number; }, m: number) => number +>c.explicitProperty = function(m) { return this.n + m } : (m: number) => number >c.explicitProperty : (this: { n: number; }, m: number) => number >c : C >explicitProperty : (this: { n: number; }, m: number) => number ->function(m) { return this.n + m } : (this: { n: number; }, m: number) => number +>function(m) { return this.n + m } : (m: number) => number >m : number >this.n + m : number >this.n : number @@ -745,11 +745,11 @@ c.explicitProperty = function(m) { return this.n + m }; >m : number c.explicitThis = function(m) { return this.n + m }; ->c.explicitThis = function(m) { return this.n + m } : (this: C, m: number) => number +>c.explicitThis = function(m) { return this.n + m } : (m: number) => number >c.explicitThis : (this: C, m: number) => number >c : C >explicitThis : (this: C, m: number) => number ->function(m) { return this.n + m } : (this: C, m: number) => number +>function(m) { return this.n + m } : (m: number) => number >m : number >this.n + m : number >this.n : number @@ -758,11 +758,11 @@ c.explicitThis = function(m) { return this.n + m }; >m : number c.implicitThis = function(m) { return this.n + m }; ->c.implicitThis = function(m) { return this.n + m } : (this: C, m: number) => number +>c.implicitThis = function(m) { return this.n + m } : (m: number) => number >c.implicitThis : (this: C, m: number) => number >c : C >implicitThis : (this: C, m: number) => number ->function(m) { return this.n + m } : (this: C, m: number) => number +>function(m) { return this.n + m } : (m: number) => number >m : number >this.n + m : number >this.n : number diff --git a/tests/cases/fourslash/quickInfoOnThis.ts b/tests/cases/fourslash/quickInfoOnThis.ts index cef78efe0cc..3213b85174e 100644 --- a/tests/cases/fourslash/quickInfoOnThis.ts +++ b/tests/cases/fourslash/quickInfoOnThis.ts @@ -42,6 +42,17 @@ ////function explicitLiteral(th/*14*/is: { n: number }): void { //// console.log(th/*15*/is); ////} +//// +//// interface ContextualInterface { +//// m: number; +//// method(this: this, n: number); +//// } +//// let o: ContextualInterface = { +//// m: 12, +//// method(n) { +//// let x = this/*16*/.m; +//// } +//// } goTo.marker('1'); verify.quickInfoIs('void'); @@ -73,4 +84,7 @@ goTo.marker('14'); verify.quickInfoIs('(parameter) this: {\n n: number;\n}'); goTo.marker('15'); -verify.quickInfoIs('this: {\n n: number;\n}'); \ No newline at end of file +verify.quickInfoIs('this: {\n n: number;\n}'); + +goTo.marker('16'); +verify.quickInfoIs('this: ContextualInterface'); \ No newline at end of file