From ad56220c4562085b0d638d731269a7b327aeffa8 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 14 Jul 2016 14:17:50 -0700 Subject: [PATCH 1/5] Instantiate contextual this parameters if needed --- src/compiler/checker.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index aea8c982a9..08ee6bf4b8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4632,6 +4632,9 @@ namespace ts { function getThisTypeOfSignature(signature: Signature): Type | undefined { if (signature.thisParameter) { + if (signature.mapper) { + signature = instantiateSignature(signature, signature.mapper); + } return getTypeOfSymbol(signature.thisParameter); } } @@ -11770,6 +11773,10 @@ namespace ts { function assignContextualParameterTypes(signature: Signature, context: Signature, mapper: TypeMapper) { const len = signature.parameters.length - (signature.hasRestParameter ? 1 : 0); + if (context.thisParameter) { + // save the mapper in case we need to type `this` later + context.mapper = mapper; + } for (let i = 0; i < len; i++) { const parameter = signature.parameters[i]; const contextualParameterType = getTypeAtPosition(context, i); From 78f807c7729f2c3cf092b539a597c58824f45d2a Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 14 Jul 2016 14:26:10 -0700 Subject: [PATCH 2/5] Test that contextually typed generic this parameters are instantiated --- ...instantiateContextuallyTypedGenericThis.js | 20 +++++++ ...ntiateContextuallyTypedGenericThis.symbols | 44 ++++++++++++++++ ...tantiateContextuallyTypedGenericThis.types | 52 +++++++++++++++++++ ...instantiateContextuallyTypedGenericThis.ts | 11 ++++ 4 files changed, 127 insertions(+) create mode 100644 tests/baselines/reference/instantiateContextuallyTypedGenericThis.js create mode 100644 tests/baselines/reference/instantiateContextuallyTypedGenericThis.symbols create mode 100644 tests/baselines/reference/instantiateContextuallyTypedGenericThis.types create mode 100644 tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts diff --git a/tests/baselines/reference/instantiateContextuallyTypedGenericThis.js b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.js new file mode 100644 index 0000000000..0233180873 --- /dev/null +++ b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.js @@ -0,0 +1,20 @@ +//// [instantiateContextuallyTypedGenericThis.ts] +interface JQuery { + each( + collection: T[], callback: (this: T, dit: T) => T + ): any; +} + +let $: JQuery; +let lines: string[]; +$.each(lines, function(dit) { + return dit.charAt(0) + this.charAt(1); +}); + + +//// [instantiateContextuallyTypedGenericThis.js] +var $; +var lines; +$.each(lines, function (dit) { + return dit.charAt(0) + this.charAt(1); +}); diff --git a/tests/baselines/reference/instantiateContextuallyTypedGenericThis.symbols b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.symbols new file mode 100644 index 0000000000..34a477b706 --- /dev/null +++ b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.symbols @@ -0,0 +1,44 @@ +=== tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts === +interface JQuery { +>JQuery : Symbol(JQuery, Decl(instantiateContextuallyTypedGenericThis.ts, 0, 0)) + + each( +>each : Symbol(JQuery.each, Decl(instantiateContextuallyTypedGenericThis.ts, 0, 18)) +>T : Symbol(T, Decl(instantiateContextuallyTypedGenericThis.ts, 1, 9)) + + collection: T[], callback: (this: T, dit: T) => T +>collection : Symbol(collection, Decl(instantiateContextuallyTypedGenericThis.ts, 1, 12)) +>T : Symbol(T, Decl(instantiateContextuallyTypedGenericThis.ts, 1, 9)) +>callback : Symbol(callback, Decl(instantiateContextuallyTypedGenericThis.ts, 2, 24)) +>this : Symbol(this, Decl(instantiateContextuallyTypedGenericThis.ts, 2, 36)) +>T : Symbol(T, Decl(instantiateContextuallyTypedGenericThis.ts, 1, 9)) +>dit : Symbol(dit, Decl(instantiateContextuallyTypedGenericThis.ts, 2, 44)) +>T : Symbol(T, Decl(instantiateContextuallyTypedGenericThis.ts, 1, 9)) +>T : Symbol(T, Decl(instantiateContextuallyTypedGenericThis.ts, 1, 9)) + + ): any; +} + +let $: JQuery; +>$ : Symbol($, Decl(instantiateContextuallyTypedGenericThis.ts, 6, 3)) +>JQuery : Symbol(JQuery, Decl(instantiateContextuallyTypedGenericThis.ts, 0, 0)) + +let lines: string[]; +>lines : Symbol(lines, Decl(instantiateContextuallyTypedGenericThis.ts, 7, 3)) + +$.each(lines, function(dit) { +>$.each : Symbol(JQuery.each, Decl(instantiateContextuallyTypedGenericThis.ts, 0, 18)) +>$ : Symbol($, Decl(instantiateContextuallyTypedGenericThis.ts, 6, 3)) +>each : Symbol(JQuery.each, Decl(instantiateContextuallyTypedGenericThis.ts, 0, 18)) +>lines : Symbol(lines, Decl(instantiateContextuallyTypedGenericThis.ts, 7, 3)) +>dit : Symbol(dit, Decl(instantiateContextuallyTypedGenericThis.ts, 8, 23)) + + return dit.charAt(0) + this.charAt(1); +>dit.charAt : Symbol(String.charAt, Decl(lib.d.ts, --, --)) +>dit : Symbol(dit, Decl(instantiateContextuallyTypedGenericThis.ts, 8, 23)) +>charAt : Symbol(String.charAt, Decl(lib.d.ts, --, --)) +>this.charAt : Symbol(String.charAt, Decl(lib.d.ts, --, --)) +>charAt : Symbol(String.charAt, Decl(lib.d.ts, --, --)) + +}); + diff --git a/tests/baselines/reference/instantiateContextuallyTypedGenericThis.types b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.types new file mode 100644 index 0000000000..060d35be21 --- /dev/null +++ b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.types @@ -0,0 +1,52 @@ +=== tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts === +interface JQuery { +>JQuery : JQuery + + each( +>each : (collection: T[], callback: (this: T, dit: T) => T) => any +>T : T + + collection: T[], callback: (this: T, dit: T) => T +>collection : T[] +>T : T +>callback : (this: T, dit: T) => T +>this : T +>T : T +>dit : T +>T : T +>T : T + + ): any; +} + +let $: JQuery; +>$ : JQuery +>JQuery : JQuery + +let lines: string[]; +>lines : string[] + +$.each(lines, function(dit) { +>$.each(lines, function(dit) { return dit.charAt(0) + this.charAt(1);}) : any +>$.each : (collection: T[], callback: (this: T, dit: T) => T) => any +>$ : JQuery +>each : (collection: T[], callback: (this: T, dit: T) => T) => any +>lines : string[] +>function(dit) { return dit.charAt(0) + this.charAt(1);} : (dit: string) => string +>dit : string + + return dit.charAt(0) + this.charAt(1); +>dit.charAt(0) + this.charAt(1) : string +>dit.charAt(0) : string +>dit.charAt : (pos: number) => string +>dit : string +>charAt : (pos: number) => string +>0 : number +>this.charAt(1) : string +>this.charAt : (pos: number) => string +>this : string +>charAt : (pos: number) => string +>1 : number + +}); + diff --git a/tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts b/tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts new file mode 100644 index 0000000000..f25e66ecfb --- /dev/null +++ b/tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts @@ -0,0 +1,11 @@ +interface JQuery { + each( + collection: T[], callback: (this: T, dit: T) => T + ): any; +} + +let $: JQuery; +let lines: string[]; +$.each(lines, function(dit) { + return dit.charAt(0) + this.charAt(1); +}); From 0f483d6546296da0dd4805eb7e7cdbdb1f436fd9 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 17 Aug 2016 14:21:49 -0700 Subject: [PATCH 3/5] Assign and instantiate contextual this type if not present --- src/compiler/checker.ts | 17 +++-- ...instantiateContextuallyTypedGenericThis.js | 2 +- ...ntiateContextuallyTypedGenericThis.symbols | 4 +- ...tantiateContextuallyTypedGenericThis.types | 13 ++-- .../reference/thisTypeInFunctions.symbols | 18 ++--- .../reference/thisTypeInFunctions.types | 70 +++++++++---------- .../reference/thisTypeInFunctions2.symbols | 7 +- .../reference/thisTypeInFunctions2.types | 6 +- ...instantiateContextuallyTypedGenericThis.ts | 2 +- 9 files changed, 72 insertions(+), 67 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1aeeb1f2c1..17cfdf8976 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4785,9 +4785,6 @@ namespace ts { function getThisTypeOfSignature(signature: Signature): Type | undefined { if (signature.thisParameter) { - if (signature.mapper) { - signature = instantiateSignature(signature, signature.mapper); - } return getTypeOfSymbol(signature.thisParameter); } } @@ -9085,15 +9082,15 @@ namespace ts { return getInferredClassType(classSymbol); } } - const type = getContextuallyTypedThisType(container); - if (type) { - return type; - } const thisType = getThisTypeOfDeclaration(container); if (thisType) { return thisType; } + const type = getContextuallyTypedThisType(container); + if (type) { + return type; + } } if (isClassLike(container.parent)) { const symbol = getSymbolOfNode(container.parent); @@ -12277,8 +12274,10 @@ namespace ts { function assignContextualParameterTypes(signature: Signature, context: Signature, mapper: TypeMapper) { const len = signature.parameters.length - (signature.hasRestParameter ? 1 : 0); if (context.thisParameter) { - // save the mapper in case we need to type `this` later - context.mapper = mapper; + if (!signature.thisParameter) { + signature.thisParameter = createTransientSymbol(context.thisParameter, undefined); + } + assignTypeToParameterAndFixTypeParameters(signature.thisParameter, getTypeOfSymbol(context.thisParameter), mapper); } for (let i = 0; i < len; i++) { const parameter = signature.parameters[i]; diff --git a/tests/baselines/reference/instantiateContextuallyTypedGenericThis.js b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.js index 0233180873..176df15985 100644 --- a/tests/baselines/reference/instantiateContextuallyTypedGenericThis.js +++ b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.js @@ -2,7 +2,7 @@ interface JQuery { each( collection: T[], callback: (this: T, dit: T) => T - ): any; + ): T[]; } let $: JQuery; diff --git a/tests/baselines/reference/instantiateContextuallyTypedGenericThis.symbols b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.symbols index 34a477b706..1db5b30dd0 100644 --- a/tests/baselines/reference/instantiateContextuallyTypedGenericThis.symbols +++ b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.symbols @@ -16,7 +16,8 @@ interface JQuery { >T : Symbol(T, Decl(instantiateContextuallyTypedGenericThis.ts, 1, 9)) >T : Symbol(T, Decl(instantiateContextuallyTypedGenericThis.ts, 1, 9)) - ): any; + ): T[]; +>T : Symbol(T, Decl(instantiateContextuallyTypedGenericThis.ts, 1, 9)) } let $: JQuery; @@ -38,6 +39,7 @@ $.each(lines, function(dit) { >dit : Symbol(dit, Decl(instantiateContextuallyTypedGenericThis.ts, 8, 23)) >charAt : Symbol(String.charAt, Decl(lib.d.ts, --, --)) >this.charAt : Symbol(String.charAt, Decl(lib.d.ts, --, --)) +>this : Symbol(this, Decl(instantiateContextuallyTypedGenericThis.ts, 2, 36)) >charAt : Symbol(String.charAt, Decl(lib.d.ts, --, --)) }); diff --git a/tests/baselines/reference/instantiateContextuallyTypedGenericThis.types b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.types index 060d35be21..5cdce6b99f 100644 --- a/tests/baselines/reference/instantiateContextuallyTypedGenericThis.types +++ b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.types @@ -3,7 +3,7 @@ interface JQuery { >JQuery : JQuery each( ->each : (collection: T[], callback: (this: T, dit: T) => T) => any +>each : (collection: T[], callback: (this: T, dit: T) => T) => T[] >T : T collection: T[], callback: (this: T, dit: T) => T @@ -16,7 +16,8 @@ interface JQuery { >T : T >T : T - ): any; + ): T[]; +>T : T } let $: JQuery; @@ -27,12 +28,12 @@ let lines: string[]; >lines : string[] $.each(lines, function(dit) { ->$.each(lines, function(dit) { return dit.charAt(0) + this.charAt(1);}) : any ->$.each : (collection: T[], callback: (this: T, dit: T) => T) => any +>$.each(lines, function(dit) { return dit.charAt(0) + this.charAt(1);}) : string[] +>$.each : (collection: T[], callback: (this: T, dit: T) => T) => T[] >$ : JQuery ->each : (collection: T[], callback: (this: T, dit: T) => T) => any +>each : (collection: T[], callback: (this: T, dit: T) => T) => T[] >lines : string[] ->function(dit) { return dit.charAt(0) + this.charAt(1);} : (dit: string) => string +>function(dit) { return dit.charAt(0) + this.charAt(1);} : (this: string, dit: string) => string >dit : string return dit.charAt(0) + this.charAt(1); diff --git a/tests/baselines/reference/thisTypeInFunctions.symbols b/tests/baselines/reference/thisTypeInFunctions.symbols index 8a0be7427e..ad16785f28 100644 --- a/tests/baselines/reference/thisTypeInFunctions.symbols +++ b/tests/baselines/reference/thisTypeInFunctions.symbols @@ -135,7 +135,7 @@ let impl: I = { return this.a; >this.a : Symbol(a, Decl(thisTypeInFunctions.ts, 24, 30)) ->this : Symbol(, Decl(thisTypeInFunctions.ts, 24, 28)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 24, 23)) >a : Symbol(a, Decl(thisTypeInFunctions.ts, 24, 30)) }, @@ -144,7 +144,7 @@ let impl: I = { return this.a; >this.a : Symbol(I.a, Decl(thisTypeInFunctions.ts, 20, 13)) ->this : Symbol(I, Decl(thisTypeInFunctions.ts, 19, 21)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 25, 22)) >a : Symbol(I.a, Decl(thisTypeInFunctions.ts, 20, 13)) }, @@ -153,7 +153,7 @@ let impl: I = { return this.a; >this.a : Symbol(I.a, Decl(thisTypeInFunctions.ts, 20, 13)) ->this : Symbol(I, Decl(thisTypeInFunctions.ts, 19, 21)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 26, 17)) >a : Symbol(I.a, Decl(thisTypeInFunctions.ts, 20, 13)) }, @@ -173,7 +173,7 @@ impl.explicitStructural = function() { return this.a; }; >impl : Symbol(impl, Decl(thisTypeInFunctions.ts, 37, 3)) >explicitStructural : Symbol(I.explicitStructural, Decl(thisTypeInFunctions.ts, 23, 38)) >this.a : Symbol(a, Decl(thisTypeInFunctions.ts, 24, 30)) ->this : Symbol(, Decl(thisTypeInFunctions.ts, 24, 28)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 24, 23)) >a : Symbol(a, Decl(thisTypeInFunctions.ts, 24, 30)) impl.explicitInterface = function() { return this.a; }; @@ -181,7 +181,7 @@ impl.explicitInterface = function() { return this.a; }; >impl : Symbol(impl, Decl(thisTypeInFunctions.ts, 37, 3)) >explicitInterface : Symbol(I.explicitInterface, Decl(thisTypeInFunctions.ts, 24, 50)) >this.a : Symbol(I.a, Decl(thisTypeInFunctions.ts, 20, 13)) ->this : Symbol(I, Decl(thisTypeInFunctions.ts, 19, 21)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 25, 22)) >a : Symbol(I.a, Decl(thisTypeInFunctions.ts, 20, 13)) impl.explicitStructural = () => 12; @@ -199,7 +199,7 @@ impl.explicitThis = function () { return this.a; }; >impl : Symbol(impl, Decl(thisTypeInFunctions.ts, 37, 3)) >explicitThis : Symbol(I.explicitThis, Decl(thisTypeInFunctions.ts, 25, 39)) >this.a : Symbol(I.a, Decl(thisTypeInFunctions.ts, 20, 13)) ->this : Symbol(I, Decl(thisTypeInFunctions.ts, 19, 21)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 26, 17)) >a : Symbol(I.a, Decl(thisTypeInFunctions.ts, 20, 13)) // parameter checking @@ -536,7 +536,7 @@ c.explicitC = function(m) { return this.n + m }; >explicitC : Symbol(C.explicitC, Decl(thisTypeInFunctions.ts, 8, 5)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 126, 23)) >this.n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 4, 9)) ->this : Symbol(C, Decl(thisTypeInFunctions.ts, 3, 1)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 9, 14)) >n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 4, 9)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 126, 23)) @@ -546,7 +546,7 @@ c.explicitProperty = function(m) { return this.n + m }; >explicitProperty : Symbol(C.explicitProperty, Decl(thisTypeInFunctions.ts, 11, 5)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 127, 30)) >this.n : Symbol(n, Decl(thisTypeInFunctions.ts, 12, 28)) ->this : Symbol(, Decl(thisTypeInFunctions.ts, 12, 26)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 12, 21)) >n : Symbol(n, Decl(thisTypeInFunctions.ts, 12, 28)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 127, 30)) @@ -556,7 +556,7 @@ c.explicitThis = function(m) { return this.n + m }; >explicitThis : Symbol(C.explicitThis, Decl(thisTypeInFunctions.ts, 5, 14)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 128, 26)) >this.n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 4, 9)) ->this : Symbol(C, Decl(thisTypeInFunctions.ts, 3, 1)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 6, 17)) >n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 4, 9)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 128, 26)) diff --git a/tests/baselines/reference/thisTypeInFunctions.types b/tests/baselines/reference/thisTypeInFunctions.types index 24c4fb87ba..863ecbce62 100644 --- a/tests/baselines/reference/thisTypeInFunctions.types +++ b/tests/baselines/reference/thisTypeInFunctions.types @@ -132,7 +132,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; },} : { a: number; explicitVoid2: () => any; explicitVoid1(): number; explicitStructural(): number; explicitInterface(): number; explicitThis(): number; } +>{ 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; },} : { a: number; explicitVoid2: () => any; explicitVoid1(this: void): number; explicitStructural(this: { a: number; }): number; explicitInterface(this: I): number; explicitThis(this: I): number; } a: 12, >a : number @@ -146,11 +146,11 @@ let impl: I = { >a : any explicitVoid1() { return 12; }, ->explicitVoid1 : () => number +>explicitVoid1 : (this: void) => number >12 : number explicitStructural() { ->explicitStructural : () => number +>explicitStructural : (this: { a: number; }) => number return this.a; >this.a : number @@ -159,7 +159,7 @@ let impl: I = { }, explicitInterface() { ->explicitInterface : () => number +>explicitInterface : (this: I) => number return this.a; >this.a : number @@ -168,7 +168,7 @@ let impl: I = { }, explicitThis() { ->explicitThis : () => number +>explicitThis : (this: I) => number return this.a; >this.a : number @@ -178,11 +178,11 @@ let impl: I = { }, } impl.explicitVoid1 = function () { return 12; }; ->impl.explicitVoid1 = function () { return 12; } : () => number +>impl.explicitVoid1 = function () { return 12; } : (this: void) => number >impl.explicitVoid1 : (this: void) => number >impl : I >explicitVoid1 : (this: void) => number ->function () { return 12; } : () => number +>function () { return 12; } : (this: void) => number >12 : number impl.explicitVoid2 = () => 12; @@ -194,21 +194,21 @@ impl.explicitVoid2 = () => 12; >12 : number impl.explicitStructural = function() { return this.a; }; ->impl.explicitStructural = function() { return this.a; } : () => number +>impl.explicitStructural = function() { return this.a; } : (this: { a: number; }) => number >impl.explicitStructural : (this: { a: number; }) => number >impl : I >explicitStructural : (this: { a: number; }) => number ->function() { return this.a; } : () => number +>function() { return this.a; } : (this: { a: number; }) => number >this.a : number >this : { a: number; } >a : number impl.explicitInterface = function() { return this.a; }; ->impl.explicitInterface = function() { return this.a; } : () => number +>impl.explicitInterface = function() { return this.a; } : (this: I) => number >impl.explicitInterface : (this: I) => number >impl : I >explicitInterface : (this: I) => number ->function() { return this.a; } : () => number +>function() { return this.a; } : (this: I) => number >this.a : number >this : I >a : number @@ -230,11 +230,11 @@ impl.explicitInterface = () => 12; >12 : number impl.explicitThis = function () { return this.a; }; ->impl.explicitThis = function () { return this.a; } : () => number +>impl.explicitThis = function () { return this.a; } : (this: I) => number >impl.explicitThis : (this: I) => number >impl : I >explicitThis : (this: I) => number ->function () { return this.a; } : () => number +>function () { return this.a; } : (this: I) => number >this.a : number >this : I >a : number @@ -433,7 +433,7 @@ let unboundToSpecified: (this: { y: number }, x: number) => number = x => x + th >this : { y: number; } >y : number >x : number ->x => x + this.y : (x: number) => any +>x => x + this.y : (this: { y: number; }, x: number) => any >x : number >x + this.y : any >x : number @@ -472,7 +472,7 @@ let specifiedLambda: (this: void, x: number) => number = x => x + 12; >specifiedLambda : (this: void, x: number) => number >this : void >x : number ->x => x + 12 : (x: number) => number +>x => x + 12 : (this: void, x: number) => number >x : number >x + 12 : number >x : number @@ -560,40 +560,40 @@ c.explicitProperty = reconstructed.explicitProperty; // lambdas are assignable to anything c.explicitC = m => m; ->c.explicitC = m => m : (m: number) => number +>c.explicitC = m => m : (this: C, m: number) => number >c.explicitC : (this: C, m: number) => number >c : C >explicitC : (this: C, m: number) => number ->m => m : (m: number) => number +>m => m : (this: C, m: number) => number >m : number >m : number c.explicitThis = m => m; ->c.explicitThis = m => m : (m: number) => number +>c.explicitThis = m => m : (this: C, m: number) => number >c.explicitThis : (this: C, m: number) => number >c : C >explicitThis : (this: C, m: number) => number ->m => m : (m: number) => number +>m => m : (this: C, m: number) => number >m : number >m : number c.explicitProperty = m => m; ->c.explicitProperty = m => m : (m: number) => number +>c.explicitProperty = m => m : (this: { n: number; }, m: number) => number >c.explicitProperty : (this: { n: number; }, m: number) => number >c : C >explicitProperty : (this: { n: number; }, m: number) => number ->m => m : (m: number) => number +>m => m : (this: { n: number; }, m: number) => number >m : number >m : number // this inside lambdas refer to outer scope // the outer-scoped lambda at top-level is still just `any` c.explicitC = m => m + this.n; ->c.explicitC = m => m + this.n : (m: number) => any +>c.explicitC = m => m + this.n : (this: C, m: number) => any >c.explicitC : (this: C, m: number) => number >c : C >explicitC : (this: C, m: number) => number ->m => m + this.n : (m: number) => any +>m => m + this.n : (this: C, m: number) => any >m : number >m + this.n : any >m : number @@ -602,11 +602,11 @@ c.explicitC = m => m + this.n; >n : any c.explicitThis = m => m + this.n; ->c.explicitThis = m => m + this.n : (m: number) => any +>c.explicitThis = m => m + this.n : (this: C, m: number) => any >c.explicitThis : (this: C, m: number) => number >c : C >explicitThis : (this: C, m: number) => number ->m => m + this.n : (m: number) => any +>m => m + this.n : (this: C, m: number) => any >m : number >m + this.n : any >m : number @@ -615,11 +615,11 @@ c.explicitThis = m => m + this.n; >n : any c.explicitProperty = m => m + this.n; ->c.explicitProperty = m => m + this.n : (m: number) => any +>c.explicitProperty = 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 ->m => m + this.n : (m: number) => any +>m => m + this.n : (this: { n: number; }, m: number) => any >m : number >m + this.n : any >m : number @@ -652,11 +652,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 } : (m: number) => number +>c.explicitC = function(m) { return this.n + m } : (this: C, m: number) => number >c.explicitC : (this: C, m: number) => number >c : C >explicitC : (this: C, m: number) => number ->function(m) { return this.n + m } : (m: number) => number +>function(m) { return this.n + m } : (this: C, m: number) => number >m : number >this.n + m : number >this.n : number @@ -665,11 +665,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 } : (m: number) => number +>c.explicitProperty = function(m) { return this.n + m } : (this: { n: number; }, 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 } : (m: number) => number +>function(m) { return this.n + m } : (this: { n: number; }, m: number) => number >m : number >this.n + m : number >this.n : number @@ -678,11 +678,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 } : (m: number) => number +>c.explicitThis = function(m) { return this.n + m } : (this: C, m: number) => number >c.explicitThis : (this: C, m: number) => number >c : C >explicitThis : (this: C, m: number) => number ->function(m) { return this.n + m } : (m: number) => number +>function(m) { return this.n + m } : (this: C, m: number) => number >m : number >this.n + m : number >this.n : number @@ -723,11 +723,11 @@ c.explicitC = function(this: B, m: number) { return this.n + m }; // this:void compatibility c.explicitVoid = n => n; ->c.explicitVoid = n => n : (n: number) => number +>c.explicitVoid = n => n : (this: void, n: number) => number >c.explicitVoid : (this: void, m: number) => number >c : C >explicitVoid : (this: void, m: number) => number ->n => n : (n: number) => number +>n => n : (this: void, n: number) => number >n : number >n : number diff --git a/tests/baselines/reference/thisTypeInFunctions2.symbols b/tests/baselines/reference/thisTypeInFunctions2.symbols index cdc7fb321d..5cc26e3ad4 100644 --- a/tests/baselines/reference/thisTypeInFunctions2.symbols +++ b/tests/baselines/reference/thisTypeInFunctions2.symbols @@ -61,12 +61,12 @@ extend1({ >init : Symbol(init, Decl(thisTypeInFunctions2.ts, 20, 9)) this // this: IndexedWithThis because of contextual typing. ->this : Symbol(IndexedWithThis, Decl(thisTypeInFunctions2.ts, 0, 0)) +>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 2, 12)) // this.mine this.willDestroy >this.willDestroy : Symbol(IndexedWithThis.willDestroy, Decl(thisTypeInFunctions2.ts, 2, 32)) ->this : Symbol(IndexedWithThis, Decl(thisTypeInFunctions2.ts, 0, 0)) +>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 2, 12)) >willDestroy : Symbol(IndexedWithThis.willDestroy, Decl(thisTypeInFunctions2.ts, 2, 32)) }, @@ -77,7 +77,10 @@ extend1({ >foo : Symbol(foo, Decl(thisTypeInFunctions2.ts, 26, 13)) this.url; // this: any because 'foo' matches the string indexer +>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 4, 87)) + this.willDestroy; +>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 4, 87)) } }); extend2({ diff --git a/tests/baselines/reference/thisTypeInFunctions2.types b/tests/baselines/reference/thisTypeInFunctions2.types index 371b5f5caf..25fb2f28a6 100644 --- a/tests/baselines/reference/thisTypeInFunctions2.types +++ b/tests/baselines/reference/thisTypeInFunctions2.types @@ -58,10 +58,10 @@ declare function simple(arg: SimpleInterface): void; extend1({ >extend1({ init() { this // this: IndexedWithThis because of contextual typing. // this.mine this.willDestroy }, mine: 12, foo() { this.url; // this: any because 'foo' matches the string indexer this.willDestroy; }}) : void >extend1 : (args: IndexedWithThis) => void ->{ init() { this // this: IndexedWithThis because of contextual typing. // this.mine this.willDestroy }, mine: 12, foo() { this.url; // this: any because 'foo' matches the string indexer this.willDestroy; }} : { init(): void; mine: number; foo(): void; } +>{ init() { this // this: IndexedWithThis because of contextual typing. // this.mine this.willDestroy }, mine: 12, foo() { this.url; // this: any because 'foo' matches the string indexer this.willDestroy; }} : { init(this: IndexedWithThis): void; mine: number; foo(this: any): void; } init() { ->init : () => void +>init : (this: IndexedWithThis) => void this // this: IndexedWithThis because of contextual typing. >this : IndexedWithThis @@ -78,7 +78,7 @@ extend1({ >12 : number foo() { ->foo : () => void +>foo : (this: any) => void this.url; // this: any because 'foo' matches the string indexer >this.url : any diff --git a/tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts b/tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts index f25e66ecfb..f06f06ee7a 100644 --- a/tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts +++ b/tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts @@ -1,7 +1,7 @@ interface JQuery { each( collection: T[], callback: (this: T, dit: T) => T - ): any; + ): T[]; } let $: JQuery; From 5aafc2c848ec209ec395428b76eb8a51341305d9 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 22 Aug 2016 14:08:34 -0700 Subject: [PATCH 4/5] Contextually type this in getDeclFromSig, not checkThisExpr --- src/compiler/checker.ts | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 17cfdf8976..81a9c639fe 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3063,9 +3063,14 @@ namespace ts { } } // Use contextual parameter type if one is available - const type = declaration.symbol.name === "this" - ? getContextuallyTypedThisType(func) - : getContextuallyTypedParameterType(declaration); + let type: Type; + if (declaration.symbol.name === "this") { + const thisParameter = getContextuallyTypedThisParameter(func); + type = thisParameter ? getTypeOfSymbol(thisParameter) : undefined; + } + else { + type = getContextuallyTypedParameterType(declaration); + } if (type) { return addOptionality(type, /*optional*/ declaration.questionToken && includeOptionality); } @@ -4689,6 +4694,9 @@ namespace ts { if (isJSConstructSignature) { minArgumentCount--; } + if (!thisParameter && isObjectLiteralMethod(declaration)) { + thisParameter = getContextuallyTypedThisParameter(declaration); + } const classType = declaration.kind === SyntaxKind.Constructor ? getDeclaredTypeOfClassOrInterface(getMergedSymbol((declaration.parent).symbol)) @@ -9087,10 +9095,6 @@ namespace ts { if (thisType) { return thisType; } - const type = getContextuallyTypedThisType(container); - if (type) { - return type; - } } if (isClassLike(container.parent)) { const symbol = getSymbolOfNode(container.parent); @@ -9326,11 +9330,11 @@ namespace ts { } } - function getContextuallyTypedThisType(func: FunctionLikeDeclaration): Type { + function getContextuallyTypedThisParameter(func: FunctionLikeDeclaration): Symbol { if (isContextSensitiveFunctionOrObjectLiteralMethod(func) && func.kind !== SyntaxKind.ArrowFunction) { const contextualSignature = getContextualSignature(func); if (contextualSignature) { - return getThisTypeOfSignature(contextualSignature); + return contextualSignature.thisParameter; } } From fc1d6a8437439b4caf232503baf07d1f5a4bf794 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 22 Aug 2016 16:36:38 -0700 Subject: [PATCH 5/5] Rename getContextuallyTypedThisParameter to getContextualThisParameter --- src/compiler/checker.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 81a9c639fe..2f2c300229 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3065,7 +3065,7 @@ namespace ts { // Use contextual parameter type if one is available let type: Type; if (declaration.symbol.name === "this") { - const thisParameter = getContextuallyTypedThisParameter(func); + const thisParameter = getContextualThisParameter(func); type = thisParameter ? getTypeOfSymbol(thisParameter) : undefined; } else { @@ -4695,7 +4695,7 @@ namespace ts { minArgumentCount--; } if (!thisParameter && isObjectLiteralMethod(declaration)) { - thisParameter = getContextuallyTypedThisParameter(declaration); + thisParameter = getContextualThisParameter(declaration); } const classType = declaration.kind === SyntaxKind.Constructor ? @@ -9330,7 +9330,7 @@ namespace ts { } } - function getContextuallyTypedThisParameter(func: FunctionLikeDeclaration): Symbol { + function getContextualThisParameter(func: FunctionLikeDeclaration): Symbol { if (isContextSensitiveFunctionOrObjectLiteralMethod(func) && func.kind !== SyntaxKind.ArrowFunction) { const contextualSignature = getContextualSignature(func); if (contextualSignature) {