mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
this in object literals intersects contextual type and literal type
This commit is contained in:
+28
-19
@@ -8179,6 +8179,21 @@ namespace ts {
|
||||
captureLexicalThis(node, container);
|
||||
}
|
||||
if (isFunctionLike(container)) {
|
||||
// If this is a function in a JS file, it might be a class method. Check if it's the RHS
|
||||
// of a x.prototype.y = function [name]() { .... }
|
||||
if (container.kind === SyntaxKind.FunctionExpression &&
|
||||
isInJavaScriptFile(container.parent) &&
|
||||
getSpecialPropertyAssignmentKind(container.parent) === SpecialPropertyAssignmentKind.PrototypeProperty) {
|
||||
// Get the 'x' of 'x.prototype.y = f' (here, 'f' is 'container')
|
||||
const className = (((container.parent as BinaryExpression) // x.prototype.y = f
|
||||
.left as PropertyAccessExpression) // x.prototype.y
|
||||
.expression as PropertyAccessExpression) // x.prototype
|
||||
.expression; // x
|
||||
const classSymbol = checkExpression(className).symbol;
|
||||
if (classSymbol && classSymbol.members && (classSymbol.flags & SymbolFlags.Function)) {
|
||||
return getInferredClassType(classSymbol);
|
||||
}
|
||||
}
|
||||
const type = getContextuallyTypedThisType(container);
|
||||
if (type) {
|
||||
return type;
|
||||
@@ -8190,9 +8205,13 @@ namespace ts {
|
||||
if (container.parent && container.parent.kind === SyntaxKind.ObjectLiteralExpression) {
|
||||
// Note: this works because object literal methods are deferred,
|
||||
// which means that the type of the containing object literal is already known.
|
||||
const type = checkExpressionCached(<ObjectLiteralExpression>container.parent);
|
||||
if (type) {
|
||||
return type;
|
||||
const contextualType = getContextualType(container.parent as ObjectLiteralExpression);
|
||||
const literalType = checkExpressionCached(<ObjectLiteralExpression>container.parent);
|
||||
if (contextualType && literalType) {
|
||||
return getIntersectionType([contextualType, literalType]);
|
||||
}
|
||||
else if (contextualType || literalType) {
|
||||
return contextualType || literalType;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8207,22 +8226,6 @@ namespace ts {
|
||||
if (type && type !== unknownType) {
|
||||
return type;
|
||||
}
|
||||
|
||||
// If this is a function in a JS file, it might be a class method. Check if it's the RHS
|
||||
// of a x.prototype.y = function [name]() { .... }
|
||||
if (container.kind === SyntaxKind.FunctionExpression) {
|
||||
if (getSpecialPropertyAssignmentKind(container.parent) === SpecialPropertyAssignmentKind.PrototypeProperty) {
|
||||
// Get the 'x' of 'x.prototype.y = f' (here, 'f' is 'container')
|
||||
const className = (((container.parent as BinaryExpression) // x.prototype.y = f
|
||||
.left as PropertyAccessExpression) // x.prototype.y
|
||||
.expression as PropertyAccessExpression) // x.prototype
|
||||
.expression; // x
|
||||
const classSymbol = checkExpression(className).symbol;
|
||||
if (classSymbol && classSymbol.members && (classSymbol.flags & SymbolFlags.Function)) {
|
||||
return getInferredClassType(classSymbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (compilerOptions.noImplicitThis) {
|
||||
@@ -8450,6 +8453,12 @@ namespace ts {
|
||||
if ((isFunctionExpressionOrArrowFunction(func) || isObjectLiteralMethod(func)) &&
|
||||
isContextSensitive(func) &&
|
||||
func.kind !== SyntaxKind.ArrowFunction) {
|
||||
const type = isObjectLiteralMethod(func)
|
||||
? getContextualTypeForObjectLiteralMethod(func)
|
||||
: getApparentTypeOfContextualType(func);
|
||||
if (type === anyType) {
|
||||
return anyType;
|
||||
}
|
||||
const contextualSignature = getContextualSignature(func);
|
||||
if (contextualSignature) {
|
||||
return contextualSignature.thisType;
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
//// [thisTypeInFunctions2.ts]
|
||||
interface Arguments {
|
||||
init?: (this: void) => 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;
|
||||
}
|
||||
|
||||
extend({
|
||||
init() {
|
||||
this
|
||||
},
|
||||
mine: 12,
|
||||
bar() {
|
||||
this.init();
|
||||
},
|
||||
foo() {
|
||||
this.bar;
|
||||
this.url
|
||||
this.handler()
|
||||
this.baz
|
||||
this.willDestroy
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
//// [thisTypeInFunctions2.js]
|
||||
var Mixin = (function () {
|
||||
function Mixin() {
|
||||
}
|
||||
return Mixin;
|
||||
}());
|
||||
extend({
|
||||
init: function () {
|
||||
this;
|
||||
},
|
||||
mine: 12,
|
||||
bar: function () {
|
||||
this.init();
|
||||
},
|
||||
foo: function () {
|
||||
this.bar;
|
||||
this.url;
|
||||
this.handler();
|
||||
this.baz;
|
||||
this.willDestroy;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
=== tests/cases/conformance/types/thisType/thisTypeInFunctions2.ts ===
|
||||
interface Arguments {
|
||||
>Arguments : Symbol(Arguments, 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))
|
||||
|
||||
willDestroy?: (this: any) => void;
|
||||
>willDestroy : Symbol(Arguments.willDestroy, Decl(thisTypeInFunctions2.ts, 1, 32))
|
||||
>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 2, 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))
|
||||
}
|
||||
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))
|
||||
|
||||
class Mixin {
|
||||
>Mixin : Symbol(Mixin, Decl(thisTypeInFunctions2.ts, 5, 52))
|
||||
|
||||
stuff: number;
|
||||
>stuff : Symbol(Mixin.stuff, Decl(thisTypeInFunctions2.ts, 6, 13))
|
||||
}
|
||||
|
||||
extend({
|
||||
>extend : Symbol(extend, Decl(thisTypeInFunctions2.ts, 4, 1))
|
||||
|
||||
init() {
|
||||
>init : Symbol(init, Decl(thisTypeInFunctions2.ts, 10, 8))
|
||||
|
||||
this
|
||||
},
|
||||
mine: 12,
|
||||
>mine : Symbol(mine, Decl(thisTypeInFunctions2.ts, 13, 6))
|
||||
|
||||
bar() {
|
||||
>bar : Symbol(bar, Decl(thisTypeInFunctions2.ts, 14, 13))
|
||||
|
||||
this.init();
|
||||
},
|
||||
foo() {
|
||||
>foo : Symbol(foo, Decl(thisTypeInFunctions2.ts, 17, 6))
|
||||
|
||||
this.bar;
|
||||
this.url
|
||||
this.handler()
|
||||
this.baz
|
||||
this.willDestroy
|
||||
}
|
||||
})
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
=== tests/cases/conformance/types/thisType/thisTypeInFunctions2.ts ===
|
||||
interface Arguments {
|
||||
>Arguments : Arguments
|
||||
|
||||
init?: (this: void) => void;
|
||||
>init : (this: void) => void
|
||||
>this : void
|
||||
|
||||
willDestroy?: (this: any) => void;
|
||||
>willDestroy : (this: any) => void
|
||||
>this : any
|
||||
|
||||
[propName: string]: number | string | boolean | symbol | undefined | null | {} | ((this: any, ...args:any[]) => any);
|
||||
>propName : string
|
||||
>null : null
|
||||
>this : any
|
||||
>args : any[]
|
||||
}
|
||||
declare function extend(arguments: Arguments): void;
|
||||
>extend : (arguments: Arguments) => void
|
||||
>arguments : Arguments
|
||||
>Arguments : Arguments
|
||||
|
||||
class Mixin {
|
||||
>Mixin : Mixin
|
||||
|
||||
stuff: number;
|
||||
>stuff : number
|
||||
}
|
||||
|
||||
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; }
|
||||
|
||||
init() {
|
||||
>init : () => void
|
||||
|
||||
this
|
||||
>this : 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 : any
|
||||
>this : any
|
||||
>url : any
|
||||
|
||||
this.handler()
|
||||
>this.handler() : any
|
||||
>this.handler : any
|
||||
>this : any
|
||||
>handler : any
|
||||
|
||||
this.baz
|
||||
>this.baz : any
|
||||
>this : any
|
||||
>baz : any
|
||||
|
||||
this.willDestroy
|
||||
>this.willDestroy : any
|
||||
>this : any
|
||||
>willDestroy : any
|
||||
}
|
||||
})
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
interface Arguments {
|
||||
init?: (this: void) => 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;
|
||||
}
|
||||
|
||||
extend({
|
||||
init() {
|
||||
this
|
||||
},
|
||||
mine: 12,
|
||||
bar() {
|
||||
this.init();
|
||||
},
|
||||
foo() {
|
||||
this.bar;
|
||||
this.url
|
||||
this.handler()
|
||||
this.baz
|
||||
this.willDestroy
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user