mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge pull request #8356 from Microsoft/smarter-object-literal-this-contextual-type
`this` in object literals intersects contextual type and literal type
This commit is contained in:
+48
-43
@@ -5508,6 +5508,10 @@ namespace ts {
|
||||
return !node.typeParameters && areAllParametersUntyped && !isNullaryArrow;
|
||||
}
|
||||
|
||||
function isContextSensitiveFunctionOrObjectLiteralMethod(func: Node): func is FunctionExpression | MethodDeclaration {
|
||||
return (isFunctionExpressionOrArrowFunction(func) || isObjectLiteralMethod(func)) && isContextSensitiveFunctionLikeDeclaration(func);
|
||||
}
|
||||
|
||||
function getTypeWithoutSignatures(type: Type): Type {
|
||||
if (type.flags & TypeFlags.ObjectType) {
|
||||
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
|
||||
@@ -8179,6 +8183,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;
|
||||
@@ -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) {
|
||||
@@ -8447,12 +8450,13 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getContextuallyTypedThisType(func: FunctionLikeDeclaration): Type {
|
||||
if ((isFunctionExpressionOrArrowFunction(func) || isObjectLiteralMethod(func)) &&
|
||||
isContextSensitive(func) &&
|
||||
func.kind !== SyntaxKind.ArrowFunction) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8462,24 +8466,21 @@ namespace ts {
|
||||
// Return contextual type of parameter or undefined if no contextual type is available
|
||||
function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type {
|
||||
const func = parameter.parent;
|
||||
if (isFunctionExpressionOrArrowFunction(func) || isObjectLiteralMethod(func)) {
|
||||
if (isContextSensitive(func)) {
|
||||
const contextualSignature = getContextualSignature(func);
|
||||
if (contextualSignature) {
|
||||
if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) {
|
||||
const contextualSignature = getContextualSignature(func);
|
||||
if (contextualSignature) {
|
||||
const funcHasRestParameters = hasRestParameter(func);
|
||||
const len = func.parameters.length - (funcHasRestParameters ? 1 : 0);
|
||||
const indexOfParameter = indexOf(func.parameters, parameter);
|
||||
if (indexOfParameter < len) {
|
||||
return getTypeAtPosition(contextualSignature, indexOfParameter);
|
||||
}
|
||||
|
||||
const funcHasRestParameters = hasRestParameter(func);
|
||||
const len = func.parameters.length - (funcHasRestParameters ? 1 : 0);
|
||||
const indexOfParameter = indexOf(func.parameters, parameter);
|
||||
if (indexOfParameter < len) {
|
||||
return getTypeAtPosition(contextualSignature, indexOfParameter);
|
||||
}
|
||||
|
||||
// If last parameter is contextually rest parameter get its type
|
||||
if (funcHasRestParameters &&
|
||||
indexOfParameter === (func.parameters.length - 1) &&
|
||||
isRestParameterIndex(contextualSignature, func.parameters.length - 1)) {
|
||||
return getTypeOfSymbol(lastOrUndefined(contextualSignature.parameters));
|
||||
}
|
||||
// If last parameter is contextually rest parameter get its type
|
||||
if (funcHasRestParameters &&
|
||||
indexOfParameter === (func.parameters.length - 1) &&
|
||||
isRestParameterIndex(contextualSignature, func.parameters.length - 1)) {
|
||||
return getTypeOfSymbol(lastOrUndefined(contextualSignature.parameters));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8487,9 +8488,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
// In a variable, parameter or property declaration with a type annotation,
|
||||
// the contextual type of an initializer expression is the type of the variable, parameter or property.
|
||||
// Otherwise, in a parameter declaration of a contextually typed function expression,
|
||||
// the contextual type of an initializer expression is the contextual type of the parameter.
|
||||
// the contextual type of an initializer expression is the type of the variable, parameter or property.
|
||||
// Otherwise, in a parameter declaration of a contextually typed function expression,
|
||||
// the contextual type of an initializer expression is the contextual type of the parameter.
|
||||
// Otherwise, in a variable or parameter declaration with a binding pattern name,
|
||||
// the contextual type of an initializer expression is the type implied by the binding pattern.
|
||||
// Otherwise, in a binding pattern inside a variable or parameter declaration,
|
||||
@@ -8843,6 +8844,12 @@ namespace ts {
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function getContextualTypeForFunctionLikeDeclaration(node: FunctionExpression | MethodDeclaration) {
|
||||
return isObjectLiteralMethod(node)
|
||||
? getContextualTypeForObjectLiteralMethod(node)
|
||||
: getApparentTypeOfContextualType(node);
|
||||
}
|
||||
|
||||
// Return the contextual signature for a given expression node. A contextual type provides a
|
||||
// contextual signature if it has a single call signature and if that call signature is non-generic.
|
||||
// If the contextual type is a union type, get the signature from each type possible and if they are
|
||||
@@ -8850,9 +8857,7 @@ namespace ts {
|
||||
// union type of return types from these signatures
|
||||
function getContextualSignature(node: FunctionExpression | MethodDeclaration): Signature {
|
||||
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
|
||||
const type = isObjectLiteralMethod(node)
|
||||
? getContextualTypeForObjectLiteralMethod(node)
|
||||
: getApparentTypeOfContextualType(node);
|
||||
const type = getContextualTypeForFunctionLikeDeclaration(node);
|
||||
if (!type) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
//// [thisTypeInFunctions2.ts]
|
||||
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);
|
||||
}
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
||||
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]
|
||||
extend1({
|
||||
init: function () {
|
||||
this; // this: IndexedWithThis because of contextual typing.
|
||||
// this.mine
|
||||
this.willDestroy;
|
||||
},
|
||||
mine: 12,
|
||||
foo: function () {
|
||||
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;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,124 @@
|
||||
=== tests/cases/conformance/types/thisType/thisTypeInFunctions2.ts ===
|
||||
interface IndexedWithThis {
|
||||
>IndexedWithThis : Symbol(IndexedWithThis, Decl(thisTypeInFunctions2.ts, 0, 0))
|
||||
|
||||
// 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(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, 4, 5))
|
||||
>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 4, 87))
|
||||
>args : Symbol(args, Decl(thisTypeInFunctions2.ts, 4, 97))
|
||||
}
|
||||
interface IndexedWithoutThis {
|
||||
>IndexedWithoutThis : Symbol(IndexedWithoutThis, Decl(thisTypeInFunctions2.ts, 5, 1))
|
||||
|
||||
// this is what React would like to write (and what they write today)
|
||||
init?: () => void;
|
||||
>init : Symbol(IndexedWithoutThis.init, Decl(thisTypeInFunctions2.ts, 6, 30))
|
||||
|
||||
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))
|
||||
|
||||
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, 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))
|
||||
|
||||
},
|
||||
mine: 12,
|
||||
>mine : Symbol(mine, Decl(thisTypeInFunctions2.ts, 25, 6))
|
||||
|
||||
foo() {
|
||||
>foo : Symbol(foo, Decl(thisTypeInFunctions2.ts, 26, 13))
|
||||
|
||||
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;
|
||||
}
|
||||
})
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
=== tests/cases/conformance/types/thisType/thisTypeInFunctions2.ts ===
|
||||
interface IndexedWithThis {
|
||||
>IndexedWithThis : IndexedWithThis
|
||||
|
||||
// this is a workaround for React
|
||||
init?: (this: this) => void;
|
||||
>init : (this: this) => void
|
||||
>this : this
|
||||
|
||||
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[]
|
||||
}
|
||||
interface IndexedWithoutThis {
|
||||
>IndexedWithoutThis : IndexedWithoutThis
|
||||
|
||||
// this is what React would like to write (and what they write today)
|
||||
init?: () => void;
|
||||
>init : () => void
|
||||
|
||||
willDestroy?: () => void;
|
||||
>willDestroy : () => void
|
||||
|
||||
[propName: string]: any;
|
||||
>propName : string
|
||||
}
|
||||
interface SimpleInterface {
|
||||
>SimpleInterface : SimpleInterface
|
||||
|
||||
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: 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
|
||||
|
||||
foo() {
|
||||
>foo : () => void
|
||||
|
||||
this.url; // this: any because 'foo' matches the string indexer
|
||||
>this.url : any
|
||||
>this : any
|
||||
>url : any
|
||||
|
||||
this.willDestroy;
|
||||
>this.willDestroy : any
|
||||
>this : 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; }
|
||||
|
||||
init() {
|
||||
>init : () => void
|
||||
|
||||
this // this: any because the contextual signature of init doesn't specify this' type
|
||||
>this : 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
|
||||
}
|
||||
})
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
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);
|
||||
}
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
||||
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;
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user