Contextual signatures without thisType return anyType

If a contextual signature is found, if its thisType is undefined, then the contextual type of `this` is now `any`.
Previously `checkThisExpression` would keep looking for a different type for `this`.

Also update tests to show this new behaviour.
This commit is contained in:
Nathan Shively-Sanders
2016-04-28 14:17:11 -07:00
parent 88854d2c0f
commit 9f7621c7e2
5 changed files with 332 additions and 118 deletions
+1 -1
View File
@@ -8453,7 +8453,7 @@ namespace ts {
if (isContextSensitiveFunctionOrObjectLiteralMethod(func) && func.kind !== SyntaxKind.ArrowFunction) {
const contextualSignature = getContextualSignature(func);
if (contextualSignature) {
return contextualSignature.thisType;
return contextualSignature.thisType || anyType;
}
else if (getContextualTypeForFunctionLikeDeclaration(func) === anyType) {
return anyType;
@@ -1,51 +1,91 @@
//// [thisTypeInFunctions2.ts]
interface Arguments {
init?: (this: void) => void;
interface IndexedWithThis {
// this is a workaround for React
init?: (this: this) => void;
willDestroy?: (this: any) => void;
[propName: string]: number | string | boolean | symbol | undefined | null | {} | ((this: any, ...args:any[]) => any);
}
declare function extend(arguments: Arguments): void;
class Mixin {
stuff: number;
interface IndexedWithoutThis {
// this is what React would like to write (and what they write today)
init?: () => void;
willDestroy?: () => void;
[propName: string]: any;
}
interface SimpleInterface {
foo(n: string);
bar(): number;
}
declare function extend1(args: IndexedWithThis): void;
declare function extend2(args: IndexedWithoutThis): void;
declare function simple(arg: SimpleInterface): void;
extend({
extend1({
init() {
this
this // this: IndexedWithThis because of contextual typing.
// this.mine
this.willDestroy
},
mine: 12,
bar() {
this.init();
},
foo() {
this.bar;
this.url
this.handler()
this.baz
this.url; // this: any because 'foo' matches the string indexer
this.willDestroy;
}
});
extend2({
init() {
this // this: any because the contextual signature of init doesn't specify this' type
this.mine
this.willDestroy
},
mine: 13,
foo() {
this // this: any because of the string indexer
this.mine
this.willDestroy
}
});
simple({
foo(n) {
return n.length + this.bar();
},
bar() {
return 14;
}
})
//// [thisTypeInFunctions2.js]
var Mixin = (function () {
function Mixin() {
}
return Mixin;
}());
extend({
extend1({
init: function () {
this;
this; // this: IndexedWithThis because of contextual typing.
// this.mine
this.willDestroy;
},
mine: 12,
bar: function () {
this.init();
},
foo: function () {
this.bar;
this.url;
this.handler();
this.baz;
this.url; // this: any because 'foo' matches the string indexer
this.willDestroy;
}
});
extend2({
init: function () {
this; // this: any because the contextual signature of init doesn't specify this' type
this.mine;
this.willDestroy;
},
mine: 13,
foo: function () {
this; // this: any because of the string indexer
this.mine;
this.willDestroy;
}
});
simple({
foo: function (n) {
return n.length + this.bar();
},
bar: function () {
return 14;
}
});
@@ -1,56 +1,124 @@
=== tests/cases/conformance/types/thisType/thisTypeInFunctions2.ts ===
interface Arguments {
>Arguments : Symbol(Arguments, Decl(thisTypeInFunctions2.ts, 0, 0))
interface IndexedWithThis {
>IndexedWithThis : Symbol(IndexedWithThis, Decl(thisTypeInFunctions2.ts, 0, 0))
init?: (this: void) => void;
>init : Symbol(Arguments.init, Decl(thisTypeInFunctions2.ts, 0, 21))
>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 1, 12))
// this is a workaround for React
init?: (this: this) => void;
>init : Symbol(IndexedWithThis.init, Decl(thisTypeInFunctions2.ts, 0, 27))
>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 2, 12))
willDestroy?: (this: any) => void;
>willDestroy : Symbol(Arguments.willDestroy, Decl(thisTypeInFunctions2.ts, 1, 32))
>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 2, 19))
>willDestroy : Symbol(IndexedWithThis.willDestroy, Decl(thisTypeInFunctions2.ts, 2, 32))
>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 3, 19))
[propName: string]: number | string | boolean | symbol | undefined | null | {} | ((this: any, ...args:any[]) => any);
>propName : Symbol(propName, Decl(thisTypeInFunctions2.ts, 3, 5))
>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 3, 87))
>args : Symbol(args, Decl(thisTypeInFunctions2.ts, 3, 97))
>propName : Symbol(propName, Decl(thisTypeInFunctions2.ts, 4, 5))
>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 4, 87))
>args : Symbol(args, Decl(thisTypeInFunctions2.ts, 4, 97))
}
declare function extend(arguments: Arguments): void;
>extend : Symbol(extend, Decl(thisTypeInFunctions2.ts, 4, 1))
>arguments : Symbol(arguments, Decl(thisTypeInFunctions2.ts, 5, 24))
>Arguments : Symbol(Arguments, Decl(thisTypeInFunctions2.ts, 0, 0))
interface IndexedWithoutThis {
>IndexedWithoutThis : Symbol(IndexedWithoutThis, Decl(thisTypeInFunctions2.ts, 5, 1))
class Mixin {
>Mixin : Symbol(Mixin, Decl(thisTypeInFunctions2.ts, 5, 52))
// this is what React would like to write (and what they write today)
init?: () => void;
>init : Symbol(IndexedWithoutThis.init, Decl(thisTypeInFunctions2.ts, 6, 30))
stuff: number;
>stuff : Symbol(Mixin.stuff, Decl(thisTypeInFunctions2.ts, 6, 13))
willDestroy?: () => void;
>willDestroy : Symbol(IndexedWithoutThis.willDestroy, Decl(thisTypeInFunctions2.ts, 8, 22))
[propName: string]: any;
>propName : Symbol(propName, Decl(thisTypeInFunctions2.ts, 10, 5))
}
interface SimpleInterface {
>SimpleInterface : Symbol(SimpleInterface, Decl(thisTypeInFunctions2.ts, 11, 1))
extend({
>extend : Symbol(extend, Decl(thisTypeInFunctions2.ts, 4, 1))
foo(n: string);
>foo : Symbol(SimpleInterface.foo, Decl(thisTypeInFunctions2.ts, 12, 27))
>n : Symbol(n, Decl(thisTypeInFunctions2.ts, 13, 8))
bar(): number;
>bar : Symbol(SimpleInterface.bar, Decl(thisTypeInFunctions2.ts, 13, 19))
}
declare function extend1(args: IndexedWithThis): void;
>extend1 : Symbol(extend1, Decl(thisTypeInFunctions2.ts, 15, 1))
>args : Symbol(args, Decl(thisTypeInFunctions2.ts, 16, 25))
>IndexedWithThis : Symbol(IndexedWithThis, Decl(thisTypeInFunctions2.ts, 0, 0))
declare function extend2(args: IndexedWithoutThis): void;
>extend2 : Symbol(extend2, Decl(thisTypeInFunctions2.ts, 16, 54))
>args : Symbol(args, Decl(thisTypeInFunctions2.ts, 17, 25))
>IndexedWithoutThis : Symbol(IndexedWithoutThis, Decl(thisTypeInFunctions2.ts, 5, 1))
declare function simple(arg: SimpleInterface): void;
>simple : Symbol(simple, Decl(thisTypeInFunctions2.ts, 17, 57))
>arg : Symbol(arg, Decl(thisTypeInFunctions2.ts, 18, 24))
>SimpleInterface : Symbol(SimpleInterface, Decl(thisTypeInFunctions2.ts, 11, 1))
extend1({
>extend1 : Symbol(extend1, Decl(thisTypeInFunctions2.ts, 15, 1))
init() {
>init : Symbol(init, Decl(thisTypeInFunctions2.ts, 10, 8))
>init : Symbol(init, Decl(thisTypeInFunctions2.ts, 20, 9))
this // this: IndexedWithThis because of contextual typing.
>this : Symbol(IndexedWithThis, Decl(thisTypeInFunctions2.ts, 0, 0))
// this.mine
this.willDestroy
>this.willDestroy : Symbol(IndexedWithThis.willDestroy, Decl(thisTypeInFunctions2.ts, 2, 32))
>this : Symbol(IndexedWithThis, Decl(thisTypeInFunctions2.ts, 0, 0))
>willDestroy : Symbol(IndexedWithThis.willDestroy, Decl(thisTypeInFunctions2.ts, 2, 32))
this
},
mine: 12,
>mine : Symbol(mine, Decl(thisTypeInFunctions2.ts, 13, 6))
>mine : Symbol(mine, Decl(thisTypeInFunctions2.ts, 25, 6))
bar() {
>bar : Symbol(bar, Decl(thisTypeInFunctions2.ts, 14, 13))
this.init();
},
foo() {
>foo : Symbol(foo, Decl(thisTypeInFunctions2.ts, 17, 6))
>foo : Symbol(foo, Decl(thisTypeInFunctions2.ts, 26, 13))
this.bar;
this.url
this.handler()
this.baz
this.url; // this: any because 'foo' matches the string indexer
this.willDestroy;
}
});
extend2({
>extend2 : Symbol(extend2, Decl(thisTypeInFunctions2.ts, 16, 54))
init() {
>init : Symbol(init, Decl(thisTypeInFunctions2.ts, 32, 9))
this // this: any because the contextual signature of init doesn't specify this' type
this.mine
this.willDestroy
},
mine: 13,
>mine : Symbol(mine, Decl(thisTypeInFunctions2.ts, 37, 6))
foo() {
>foo : Symbol(foo, Decl(thisTypeInFunctions2.ts, 38, 13))
this // this: any because of the string indexer
this.mine
this.willDestroy
}
});
simple({
>simple : Symbol(simple, Decl(thisTypeInFunctions2.ts, 17, 57))
foo(n) {
>foo : Symbol(foo, Decl(thisTypeInFunctions2.ts, 46, 8))
>n : Symbol(n, Decl(thisTypeInFunctions2.ts, 47, 8))
return n.length + this.bar();
>n.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>n : Symbol(n, Decl(thisTypeInFunctions2.ts, 47, 8))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
},
bar() {
>bar : Symbol(bar, Decl(thisTypeInFunctions2.ts, 49, 6))
return 14;
}
})
@@ -1,10 +1,11 @@
=== tests/cases/conformance/types/thisType/thisTypeInFunctions2.ts ===
interface Arguments {
>Arguments : Arguments
interface IndexedWithThis {
>IndexedWithThis : IndexedWithThis
init?: (this: void) => void;
>init : (this: void) => void
>this : void
// this is a workaround for React
init?: (this: this) => void;
>init : (this: this) => void
>this : this
willDestroy?: (this: any) => void;
>willDestroy : (this: any) => void
@@ -16,72 +17,149 @@ interface Arguments {
>this : any
>args : any[]
}
declare function extend(arguments: Arguments): void;
>extend : (arguments: Arguments) => void
>arguments : Arguments
>Arguments : Arguments
interface IndexedWithoutThis {
>IndexedWithoutThis : IndexedWithoutThis
class Mixin {
>Mixin : Mixin
// this is what React would like to write (and what they write today)
init?: () => void;
>init : () => void
stuff: number;
>stuff : number
willDestroy?: () => void;
>willDestroy : () => void
[propName: string]: any;
>propName : string
}
interface SimpleInterface {
>SimpleInterface : SimpleInterface
extend({
>extend({ init() { this }, mine: 12, bar() { this.init(); }, foo() { this.bar; this.url this.handler() this.baz this.willDestroy }}) : void
>extend : (arguments: Arguments) => void
>{ init() { this }, mine: 12, bar() { this.init(); }, foo() { this.bar; this.url this.handler() this.baz this.willDestroy }} : { init(): void; mine: number; bar(): void; foo(): void; }
foo(n: string);
>foo : (n: string) => any
>n : string
bar(): number;
>bar : () => number
}
declare function extend1(args: IndexedWithThis): void;
>extend1 : (args: IndexedWithThis) => void
>args : IndexedWithThis
>IndexedWithThis : IndexedWithThis
declare function extend2(args: IndexedWithoutThis): void;
>extend2 : (args: IndexedWithoutThis) => void
>args : IndexedWithoutThis
>IndexedWithoutThis : IndexedWithoutThis
declare function simple(arg: SimpleInterface): void;
>simple : (arg: SimpleInterface) => void
>arg : SimpleInterface
>SimpleInterface : SimpleInterface
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() {
>init : () => void
this
>this : void
this // this: IndexedWithThis because of contextual typing.
>this : IndexedWithThis
// this.mine
this.willDestroy
>this.willDestroy : (this: any) => void
>this : IndexedWithThis
>willDestroy : (this: any) => void
},
mine: 12,
>mine : number
>12 : number
bar() {
>bar : () => void
this.init();
>this.init() : any
>this.init : any
>this : any
>init : any
},
foo() {
>foo : () => void
this.bar;
>this.bar : any
>this : any
>bar : any
this.url
this.url; // this: any because 'foo' matches the string indexer
>this.url : any
>this : any
>url : any
this.handler()
>this.handler() : any
>this.handler : any
this.willDestroy;
>this.willDestroy : any
>this : any
>handler : any
>willDestroy : any
}
});
extend2({
>extend2({ init() { this // this: any because the contextual signature of init doesn't specify this' type this.mine this.willDestroy }, mine: 13, foo() { this // this: any because of the string indexer this.mine this.willDestroy }}) : void
>extend2 : (args: IndexedWithoutThis) => void
>{ init() { this // this: any because the contextual signature of init doesn't specify this' type this.mine this.willDestroy }, mine: 13, foo() { this // this: any because of the string indexer this.mine this.willDestroy }} : { init(): void; mine: number; foo(): void; }
this.baz
>this.baz : any
init() {
>init : () => void
this // this: any because the contextual signature of init doesn't specify this' type
>this : any
>baz : any
this.mine
>this.mine : any
>this : any
>mine : any
this.willDestroy
>this.willDestroy : any
>this : any
>willDestroy : any
},
mine: 13,
>mine : number
>13 : number
foo() {
>foo : () => void
this // this: any because of the string indexer
>this : any
this.mine
>this.mine : any
>this : any
>mine : any
this.willDestroy
>this.willDestroy : any
>this : any
>willDestroy : any
}
});
simple({
>simple({ foo(n) { return n.length + this.bar(); }, bar() { return 14; }}) : void
>simple : (arg: SimpleInterface) => void
>{ foo(n) { return n.length + this.bar(); }, bar() { return 14; }} : { foo(n: string): any; bar(): number; }
foo(n) {
>foo : (n: string) => any
>n : string
return n.length + this.bar();
>n.length + this.bar() : any
>n.length : number
>n : string
>length : number
>this.bar() : any
>this.bar : any
>this : any
>bar : any
},
bar() {
>bar : () => number
return 14;
>14 : number
}
})
@@ -1,26 +1,54 @@
interface Arguments {
init?: (this: void) => void;
interface IndexedWithThis {
// this is a workaround for React
init?: (this: this) => void;
willDestroy?: (this: any) => void;
[propName: string]: number | string | boolean | symbol | undefined | null | {} | ((this: any, ...args:any[]) => any);
}
declare function extend(arguments: Arguments): void;
class Mixin {
stuff: number;
interface IndexedWithoutThis {
// this is what React would like to write (and what they write today)
init?: () => void;
willDestroy?: () => void;
[propName: string]: any;
}
interface SimpleInterface {
foo(n: string);
bar(): number;
}
declare function extend1(args: IndexedWithThis): void;
declare function extend2(args: IndexedWithoutThis): void;
declare function simple(arg: SimpleInterface): void;
extend({
extend1({
init() {
this
this // this: IndexedWithThis because of contextual typing.
// this.mine
this.willDestroy
},
mine: 12,
bar() {
this.init();
},
foo() {
this.bar;
this.url
this.handler()
this.baz
this.url; // this: any because 'foo' matches the string indexer
this.willDestroy;
}
});
extend2({
init() {
this // this: any because the contextual signature of init doesn't specify this' type
this.mine
this.willDestroy
},
mine: 13,
foo() {
this // this: any because of the string indexer
this.mine
this.willDestroy
}
});
simple({
foo(n) {
return n.length + this.bar();
},
bar() {
return 14;
}
})