From 8c87da523bd927a5579f6286946339ccc6d52de5 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 4 Feb 2016 15:43:43 -0800 Subject: [PATCH] First round of review comments addressed. Only major thing is a bug fix in `isContextSensitiveFunctionLikeDeclaration`, and turning on context sensitivity to `this` even with `--strictThis` off. --- src/compiler/checker.ts | 66 ++++++++++--------- .../reference/contextualTyping24.errors.txt | 2 +- .../baselines/reference/contextualTyping24.js | 2 +- .../looseThisTypeInFunctions.errors.txt | 32 +++++++-- .../reference/looseThisTypeInFunctions.js | 31 ++++++++- .../reference/thisTypeInFunctions.js | 8 +-- .../reference/thisTypeInFunctions.symbols | 8 +-- .../reference/thisTypeInFunctions.types | 26 ++++---- .../thisTypeInFunctionsNegative.errors.txt | 6 +- tests/cases/compiler/contextualTyping24.ts | 2 +- .../thisType/looseThisTypeInFunctions.ts | 17 ++++- .../types/thisType/thisTypeInFuncTemp.ts | 27 ++++++++ .../types/thisType/thisTypeInFunctions.ts | 8 +-- 13 files changed, 162 insertions(+), 73 deletions(-) create mode 100644 tests/cases/conformance/types/thisType/thisTypeInFuncTemp.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9188cfff55d..2303aa539f6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -131,8 +131,8 @@ namespace ts { const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - const anySignature = createSignature(undefined, undefined, emptyArray, undefined, anyType, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false); - const unknownSignature = createSignature(undefined, undefined, emptyArray, undefined, unknownType, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false); + const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false); + const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false); const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true); @@ -3540,7 +3540,7 @@ namespace ts { resolveObjectTypeMembers(type, source, typeParameters, typeArguments); } - function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], parameters: Symbol[], thisType: Type, + function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], thisType: Type, parameters: Symbol[], resolvedReturnType: Type, minArgumentCount: number, hasRestParameter: boolean, hasStringLiterals: boolean): Signature { const sig = new Signature(checker); sig.declaration = declaration; @@ -3555,7 +3555,7 @@ namespace ts { } function cloneSignature(sig: Signature): Signature { - return createSignature(sig.declaration, sig.typeParameters, sig.parameters, sig.thisType, sig.resolvedReturnType, + return createSignature(sig.declaration, sig.typeParameters, sig.thisType, sig.parameters, sig.resolvedReturnType, sig.minArgumentCount, sig.hasRestParameter, sig.hasStringLiterals); } @@ -3567,7 +3567,7 @@ namespace ts { const baseConstructorType = getBaseConstructorTypeOfClass(classType); const baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct); if (baseSignatures.length === 0) { - return [createSignature(undefined, classType.localTypeParameters, emptyArray, undefined, classType, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false)]; + return [createSignature(undefined, classType.localTypeParameters, undefined, emptyArray, classType, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false)]; } const baseTypeNode = getBaseTypeNodeOfClass(classType); const typeArguments = map(baseTypeNode.typeArguments, getTypeFromTypeNode); @@ -4098,6 +4098,7 @@ namespace ts { let hasStringLiterals = false; let minArgumentCount = -1; let thisType: Type = undefined; + let hasThisParameter: boolean; const isJSConstructSignature = isJSDocConstructSignature(declaration); let returnType: Type = undefined; @@ -4113,11 +4114,9 @@ namespace ts { const resolvedSymbol = resolveName(param, paramSymbol.name, SymbolFlags.Value, undefined, undefined); paramSymbol = resolvedSymbol; } - if (paramSymbol.name === "this") { - thisType = param.type && getTypeOfSymbol(paramSymbol); - if (i !== 0 || declaration.kind === SyntaxKind.Constructor) { - error(param, Diagnostics.this_cannot_be_referenced_in_current_location); - } + if (i == 0 && paramSymbol.name === "this") { + hasThisParameter = true; + thisType = param.type ? getTypeFromTypeNode(param.type) : unknownType; } else { parameters.push(paramSymbol); @@ -4129,7 +4128,7 @@ namespace ts { if (param.initializer || param.questionToken || param.dotDotDotToken) { if (minArgumentCount < 0) { - minArgumentCount = i - (thisType ? 1 : 0); + minArgumentCount = i - (hasThisParameter ? 1 : 0); } } else { @@ -4139,19 +4138,19 @@ namespace ts { } if (minArgumentCount < 0) { - minArgumentCount = declaration.parameters.length - (thisType ? 1 : 0); + minArgumentCount = declaration.parameters.length - (hasThisParameter ? 1 : 0); } - if (!thisType && compilerOptions.strictThis) { - if (declaration.kind === SyntaxKind.FunctionDeclaration - || declaration.kind === SyntaxKind.CallSignature - || declaration.kind == SyntaxKind.FunctionExpression - || declaration.kind === SyntaxKind.FunctionType) { + if (!hasThisParameter && compilerOptions.strictThis) { + if (declaration.kind === SyntaxKind.FunctionDeclaration || + declaration.kind === SyntaxKind.CallSignature || + declaration.kind == SyntaxKind.FunctionExpression || + declaration.kind === SyntaxKind.FunctionType) { thisType = voidType; } else if ((declaration.kind === SyntaxKind.MethodDeclaration || declaration.kind === SyntaxKind.MethodSignature) && (isClassLike(declaration.parent) || declaration.parent.kind === SyntaxKind.InterfaceDeclaration)) { thisType = declaration.flags & NodeFlags.Static ? - getWidenedType(checkExpression((declaration.parent).name)) : + getTypeOfSymbol(getSymbolOfNode(declaration.parent)) : getThisType(declaration.name); Debug.assert(!!thisType, "couldn't find implicit this type"); } @@ -4187,7 +4186,7 @@ namespace ts { } } - links.resolvedSignature = createSignature(declaration, typeParameters, parameters, thisType, returnType, minArgumentCount, hasRestParameter(declaration), hasStringLiterals); + links.resolvedSignature = createSignature(declaration, typeParameters, thisType, parameters, returnType, minArgumentCount, hasRestParameter(declaration), hasStringLiterals); } return links.resolvedSignature; } @@ -5105,8 +5104,8 @@ namespace ts { } } const result = createSignature(signature.declaration, freshTypeParameters, + signature.thisType && instantiateType(signature.thisType, mapper), instantiateList(signature.parameters, mapper, instantiateSymbol), - signature.thisType ? instantiateType(signature.thisType, mapper) : undefined, instantiateType(signature.resolvedReturnType, mapper), signature.minArgumentCount, signature.hasRestParameter, signature.hasStringLiterals); result.target = signature; @@ -5220,14 +5219,12 @@ namespace ts { } function isContextSensitiveFunctionLikeDeclaration(node: FunctionLikeDeclaration) { - if (compilerOptions.strictThis) { - return !node.typeParameters && - (!forEach(node.parameters, p => p.type) - || (node.kind !== SyntaxKind.ArrowFunction && (!node.parameters.length || (node.parameters[0].name).text !== "this"))); - } - else { - return !node.typeParameters && node.parameters.length && !forEach(node.parameters, p => p.type); + const areAllParametersUntyped = !forEach(node.parameters, p => p.type); + if (node.kind === SyntaxKind.ArrowFunction) { + return !node.typeParameters && node.parameters.length && areAllParametersUntyped; } + const hasThisType = node.parameters.length && (node.parameters[0].name).text === "this" && node.parameters[0].type; + return !node.typeParameters && areAllParametersUntyped && !hasThisType; } function getTypeWithoutSignatures(type: Type): Type { @@ -5305,13 +5302,13 @@ namespace ts { let result = Ternary.True; if (source.thisType || target.thisType) { - const s = source.thisType || anyType; - const t = target.thisType || anyType; - if (s !== voidType) { + if (source.thisType !== voidType) { + const s = source.thisType ? getApparentType(source.thisType) : anyType; + const t = target.thisType ? getApparentType(target.thisType) : anyType; // void sources are assignable to anything. - let related = compareTypes(getApparentType(t), getApparentType(s), reportErrors); + let related = compareTypes(t, s, reportErrors); if (!related) { - related = compareTypes(getApparentType(s), getApparentType(t), /*reportErrors*/ false); + related = compareTypes(s, t, /*reportErrors*/ false); if (!related) { errorReporter(Diagnostics.Types_of_parameters_0_and_1_are_incompatible, "this", "this"); return Ternary.False; @@ -11626,6 +11623,11 @@ namespace ts { if (node.questionToken && isBindingPattern(node.name) && func.body) { error(node, Diagnostics.A_binding_pattern_parameter_cannot_be_optional_in_an_implementation_signature); } + if ((node.name).text === "this") { + if(indexOf(func.parameters, node) !== 0 || func.kind === SyntaxKind.Constructor) { + error(node, Diagnostics.this_cannot_be_referenced_in_current_location); + } + } // Only check rest parameter type if it's not a binding pattern. Since binding patterns are // not allowed in a rest parameter, we already have an error from checkGrammarParameterList. diff --git a/tests/baselines/reference/contextualTyping24.errors.txt b/tests/baselines/reference/contextualTyping24.errors.txt index a172600e1c5..b4d0d534456 100644 --- a/tests/baselines/reference/contextualTyping24.errors.txt +++ b/tests/baselines/reference/contextualTyping24.errors.txt @@ -4,7 +4,7 @@ tests/cases/compiler/contextualTyping24.ts(1,55): error TS2322: Type '(a: string ==== tests/cases/compiler/contextualTyping24.ts (1 errors) ==== - var foo:(a:{():number; (i:number):number; })=>number; foo = function(a:string){return 5}; + var foo:(a:{():number; (i:number):number; })=>number; foo = function(this: void, a:string){return 5}; ~~~ !!! error TS2322: Type '(a: string) => number' is not assignable to type '(a: { (): number; (i: number): number; }) => number'. !!! error TS2322: Types of parameters 'a' and 'a' are incompatible. diff --git a/tests/baselines/reference/contextualTyping24.js b/tests/baselines/reference/contextualTyping24.js index 04c4ecba21b..14c1feeb031 100644 --- a/tests/baselines/reference/contextualTyping24.js +++ b/tests/baselines/reference/contextualTyping24.js @@ -1,5 +1,5 @@ //// [contextualTyping24.ts] -var foo:(a:{():number; (i:number):number; })=>number; foo = function(a:string){return 5}; +var foo:(a:{():number; (i:number):number; })=>number; foo = function(this: void, a:string){return 5}; //// [contextualTyping24.js] var foo; diff --git a/tests/baselines/reference/looseThisTypeInFunctions.errors.txt b/tests/baselines/reference/looseThisTypeInFunctions.errors.txt index 058a1555ed6..317446050a8 100644 --- a/tests/baselines/reference/looseThisTypeInFunctions.errors.txt +++ b/tests/baselines/reference/looseThisTypeInFunctions.errors.txt @@ -1,11 +1,15 @@ -tests/cases/conformance/types/thisType/looseThisTypeInFunctions.ts(20,1): error TS2322: Type '(this: C, m: number) => number' is not assignable to type '(m: number) => number'. +tests/cases/conformance/types/thisType/looseThisTypeInFunctions.ts(21,1): error TS2322: Type '(this: C, m: number) => number' is not assignable to type '(m: number) => number'. Types of parameters 'this' and 'this' are incompatible. Type 'void' is not assignable to type 'C'. -tests/cases/conformance/types/thisType/looseThisTypeInFunctions.ts(27,9): error TS2345: Argument of type 'void' is not assignable to parameter of type 'I'. +tests/cases/conformance/types/thisType/looseThisTypeInFunctions.ts(32,5): error TS1005: ',' expected. +tests/cases/conformance/types/thisType/looseThisTypeInFunctions.ts(33,27): error TS2339: Property 'length' does not exist on type 'number'. +tests/cases/conformance/types/thisType/looseThisTypeInFunctions.ts(37,9): error TS2345: Argument of type 'void' is not assignable to parameter of type 'I'. +tests/cases/conformance/types/thisType/looseThisTypeInFunctions.ts(46,19): error TS2339: Property 'length' does not exist on type 'number'. -==== tests/cases/conformance/types/thisType/looseThisTypeInFunctions.ts (2 errors) ==== +==== tests/cases/conformance/types/thisType/looseThisTypeInFunctions.ts (5 errors) ==== interface I { + n: number; explicitThis(this: this, m: number): number; } interface Unused { @@ -30,10 +34,23 @@ tests/cases/conformance/types/thisType/looseThisTypeInFunctions.ts(27,9): error !!! error TS2322: Types of parameters 'this' and 'this' are incompatible. !!! error TS2322: Type 'void' is not assignable to type 'C'. let o = { - explicitThis: function (m) { return m }, - implicitThis(m: number): number { return m } + n: 101, + explicitThis: function (m: number) { + return m + this.n.length; // ok, this.n: any + }, + implicitThis(m: number): number { return m; } }; let i: I = o; + let o2: I = { + n: 1001 + explicitThis: function (m) { + ~~~~~~~~~~~~ +!!! error TS1005: ',' expected. + return m + this.n.length; // error, this.n: number, no member 'length' + ~~~~~~ +!!! error TS2339: Property 'length' does not exist on type 'number'. + }, + } let x = i.explicitThis; let n = x(12); // callee:void doesn't match this:I ~~~~~ @@ -45,4 +62,9 @@ tests/cases/conformance/types/thisType/looseThisTypeInFunctions.ts(27,9): error o.implicitThis = c.implicitThis; // ok, implicitThis(this:any) o.implicitThis = c.explicitThis; // ok, implicitThis(this:any) is assignable to explicitThis(this: this) o.implicitThis = i.explicitThis; + i.explicitThis = function(m) { + return this.n.length; // error, this.n: number + ~~~~~~ +!!! error TS2339: Property 'length' does not exist on type 'number'. + } \ No newline at end of file diff --git a/tests/baselines/reference/looseThisTypeInFunctions.js b/tests/baselines/reference/looseThisTypeInFunctions.js index 66677293c5b..ecb9f650716 100644 --- a/tests/baselines/reference/looseThisTypeInFunctions.js +++ b/tests/baselines/reference/looseThisTypeInFunctions.js @@ -1,5 +1,6 @@ //// [looseThisTypeInFunctions.ts] interface I { + n: number; explicitThis(this: this, m: number): number; } interface Unused { @@ -20,10 +21,19 @@ class C implements I { let c = new C(); c.explicitVoid = c.explicitThis; // error, 'void' is missing everything let o = { - explicitThis: function (m) { return m }, - implicitThis(m: number): number { return m } + n: 101, + explicitThis: function (m: number) { + return m + this.n.length; // ok, this.n: any + }, + implicitThis(m: number): number { return m; } }; let i: I = o; +let o2: I = { + n: 1001 + explicitThis: function (m) { + return m + this.n.length; // error, this.n: number, no member 'length' + }, +} let x = i.explicitThis; let n = x(12); // callee:void doesn't match this:I let u: Unused; @@ -33,6 +43,9 @@ c.explicitVoid = c.implicitThis // ok, implicitThis(this:any) o.implicitThis = c.implicitThis; // ok, implicitThis(this:any) o.implicitThis = c.explicitThis; // ok, implicitThis(this:any) is assignable to explicitThis(this: this) o.implicitThis = i.explicitThis; +i.explicitThis = function(m) { + return this.n.length; // error, this.n: number +} //// [looseThisTypeInFunctions.js] @@ -53,10 +66,19 @@ var C = (function () { var c = new C(); c.explicitVoid = c.explicitThis; // error, 'void' is missing everything var o = { - explicitThis: function (m) { return m; }, + n: 101, + explicitThis: function (m) { + return m + this.n.length; // ok, this.n: any + }, implicitThis: function (m) { return m; } }; var i = o; +var o2 = { + n: 1001, + explicitThis: function (m) { + return m + this.n.length; // error, this.n: number, no member 'length' + } +}; var x = i.explicitThis; var n = x(12); // callee:void doesn't match this:I var u; @@ -66,3 +88,6 @@ c.explicitVoid = c.implicitThis; // ok, implicitThis(this:any) o.implicitThis = c.implicitThis; // ok, implicitThis(this:any) o.implicitThis = c.explicitThis; // ok, implicitThis(this:any) is assignable to explicitThis(this: this) o.implicitThis = i.explicitThis; +i.explicitThis = function (m) { + return this.n.length; // error, this.n: number +}; diff --git a/tests/baselines/reference/thisTypeInFunctions.js b/tests/baselines/reference/thisTypeInFunctions.js index 0798843fc44..f3ad84acb55 100644 --- a/tests/baselines/reference/thisTypeInFunctions.js +++ b/tests/baselines/reference/thisTypeInFunctions.js @@ -140,10 +140,10 @@ c.explicitThis = explicitCFunction; c.explicitThis = function(this: C, m: number) { return this.n + m }; // this:any compatibility -c.explicitC = function(m: number) { return this.n + m }; -c.explicitProperty = function(m: number) { return this.n + m }; -c.explicitThis = function(m: number) { return this.n + m }; -c.implicitThis = function(m: number) { return this.n + m }; +c.explicitC = function(m) { return this.n + m }; +c.explicitProperty = function(m) { return this.n + m }; +c.explicitThis = function(m) { return this.n + m }; +c.implicitThis = function(m) { return this.n + m }; c.implicitThis = reconstructed.implicitThis; c.explicitC = function(this: B, m: number) { return this.n + m }; diff --git a/tests/baselines/reference/thisTypeInFunctions.symbols b/tests/baselines/reference/thisTypeInFunctions.symbols index 1d9ebbd0cf4..bb6a9acfdd0 100644 --- a/tests/baselines/reference/thisTypeInFunctions.symbols +++ b/tests/baselines/reference/thisTypeInFunctions.symbols @@ -585,7 +585,7 @@ c.explicitThis = function(this: C, m: number) { return this.n + m }; >m : Symbol(m, Decl(thisTypeInFunctions.ts, 138, 34)) // this:any compatibility -c.explicitC = function(m: number) { return this.n + m }; +c.explicitC = function(m) { return this.n + m }; >c.explicitC : Symbol(C.explicitC, Decl(thisTypeInFunctions.ts, 8, 5)) >c : Symbol(c, Decl(thisTypeInFunctions.ts, 77, 3)) >explicitC : Symbol(C.explicitC, Decl(thisTypeInFunctions.ts, 8, 5)) @@ -595,7 +595,7 @@ c.explicitC = function(m: number) { return this.n + m }; >n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 1, 9)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 141, 23)) -c.explicitProperty = function(m: number) { return this.n + m }; +c.explicitProperty = function(m) { return this.n + m }; >c.explicitProperty : Symbol(C.explicitProperty, Decl(thisTypeInFunctions.ts, 11, 5)) >c : Symbol(c, Decl(thisTypeInFunctions.ts, 77, 3)) >explicitProperty : Symbol(C.explicitProperty, Decl(thisTypeInFunctions.ts, 11, 5)) @@ -605,7 +605,7 @@ c.explicitProperty = function(m: number) { return this.n + m }; >n : Symbol(n, Decl(thisTypeInFunctions.ts, 12, 28)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 142, 30)) -c.explicitThis = function(m: number) { return this.n + m }; +c.explicitThis = function(m) { return this.n + m }; >c.explicitThis : Symbol(C.explicitThis, Decl(thisTypeInFunctions.ts, 2, 14)) >c : Symbol(c, Decl(thisTypeInFunctions.ts, 77, 3)) >explicitThis : Symbol(C.explicitThis, Decl(thisTypeInFunctions.ts, 2, 14)) @@ -615,7 +615,7 @@ c.explicitThis = function(m: number) { return this.n + m }; >n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 1, 9)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 143, 26)) -c.implicitThis = function(m: number) { return this.n + m }; +c.implicitThis = function(m) { return this.n + m }; >c.implicitThis : Symbol(C.implicitThis, Decl(thisTypeInFunctions.ts, 5, 5)) >c : Symbol(c, Decl(thisTypeInFunctions.ts, 77, 3)) >implicitThis : Symbol(C.implicitThis, Decl(thisTypeInFunctions.ts, 5, 5)) diff --git a/tests/baselines/reference/thisTypeInFunctions.types b/tests/baselines/reference/thisTypeInFunctions.types index 36b458b91e0..b0fc9fc3300 100644 --- a/tests/baselines/reference/thisTypeInFunctions.types +++ b/tests/baselines/reference/thisTypeInFunctions.types @@ -520,7 +520,7 @@ let anyToSpecified: (this: { y: number }, x: number) => number = function(x: num >this : { y: number; } >y : number >x : number ->function(x: number): number { return x + 12; } : (this: { y: number; }, x: number) => number +>function(x: number): number { return x + 12; } : (x: number) => number >x : number >x + 12 : number >x : number @@ -718,12 +718,12 @@ c.explicitThis = function(this: C, m: number) { return this.n + m }; >m : number // this:any compatibility -c.explicitC = function(m: number) { return this.n + m }; ->c.explicitC = function(m: number) { return this.n + m } : (this: C, m: number) => number +c.explicitC = function(m) { return this.n + m }; +>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: number) { return this.n + m } : (this: C, m: number) => number +>function(m) { return this.n + m } : (this: C, m: number) => number >m : number >this.n + m : number >this.n : number @@ -731,12 +731,12 @@ c.explicitC = function(m: number) { return this.n + m }; >n : number >m : number -c.explicitProperty = function(m: number) { return this.n + m }; ->c.explicitProperty = function(m: number) { return this.n + m } : (this: { n: number; }, m: number) => 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 : (this: { n: number; }, m: number) => number >c : C >explicitProperty : (this: { n: number; }, m: number) => number ->function(m: number) { return this.n + m } : (this: { n: number; }, m: number) => number +>function(m) { return this.n + m } : (this: { n: number; }, m: number) => number >m : number >this.n + m : number >this.n : number @@ -744,12 +744,12 @@ c.explicitProperty = function(m: number) { return this.n + m }; >n : number >m : number -c.explicitThis = function(m: number) { return this.n + m }; ->c.explicitThis = function(m: number) { return this.n + m } : (this: C, m: number) => number +c.explicitThis = function(m) { return this.n + m }; +>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: number) { return this.n + m } : (this: C, m: number) => number +>function(m) { return this.n + m } : (this: C, m: number) => number >m : number >this.n + m : number >this.n : number @@ -757,12 +757,12 @@ c.explicitThis = function(m: number) { return this.n + m }; >n : number >m : number -c.implicitThis = function(m: number) { return this.n + m }; ->c.implicitThis = function(m: number) { return this.n + m } : (this: C, m: number) => number +c.implicitThis = function(m) { return this.n + m }; +>c.implicitThis = function(m) { return this.n + m } : (this: C, m: number) => number >c.implicitThis : (this: C, m: number) => number >c : C >implicitThis : (this: C, m: number) => number ->function(m: number) { return this.n + m } : (this: C, m: number) => number +>function(m) { return this.n + m } : (this: C, m: number) => number >m : number >this.n + m : number >this.n : number diff --git a/tests/baselines/reference/thisTypeInFunctionsNegative.errors.txt b/tests/baselines/reference/thisTypeInFunctionsNegative.errors.txt index ce3b3eed6f8..4e8efd187eb 100644 --- a/tests/baselines/reference/thisTypeInFunctionsNegative.errors.txt +++ b/tests/baselines/reference/thisTypeInFunctionsNegative.errors.txt @@ -94,6 +94,7 @@ tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(170,1): er tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(179,16): error TS2672: A function that is called with the 'new' keyword cannot have a 'this' type that is void. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(180,24): error TS2672: A function that is called with the 'new' keyword cannot have a 'this' type that is void. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(184,30): error TS2332: 'this' cannot be referenced in current location. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(184,61): error TS2339: Property 'n' does not exist on type 'void'. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(185,26): error TS1003: Identifier expected. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(185,30): error TS1005: ',' expected. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(185,57): error TS2339: Property 'n' does not exist on type 'void'. @@ -103,7 +104,6 @@ tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(186,27): e tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(186,54): error TS2339: Property 'n' does not exist on type 'void'. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(187,23): error TS1005: ',' expected. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(187,24): error TS1138: Parameter declaration expected. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(187,51): error TS2339: Property 'n' does not exist on type 'void'. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(188,28): error TS1003: Identifier expected. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(188,32): error TS1005: ',' expected. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(188,59): error TS2339: Property 'n' does not exist on type 'void'. @@ -452,6 +452,8 @@ tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(192,35): e function notFirst(a: number, this: C): number { return this.n; } ~~~~~~~ !!! error TS2332: 'this' cannot be referenced in current location. + ~ +!!! error TS2339: Property 'n' does not exist on type 'void'. function modifiers(async this: C): number { return this.n; } ~~~~ !!! error TS1003: Identifier expected. @@ -473,8 +475,6 @@ tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(192,35): e !!! error TS1005: ',' expected. ~ !!! error TS1138: Parameter declaration expected. - ~ -!!! error TS2339: Property 'n' does not exist on type 'void'. function decorated(@deco() this: C): number { return this.n; } ~~~~ !!! error TS1003: Identifier expected. diff --git a/tests/cases/compiler/contextualTyping24.ts b/tests/cases/compiler/contextualTyping24.ts index be28ff3b04c..fad23fa313c 100644 --- a/tests/cases/compiler/contextualTyping24.ts +++ b/tests/cases/compiler/contextualTyping24.ts @@ -1 +1 @@ -var foo:(a:{():number; (i:number):number; })=>number; foo = function(a:string){return 5}; \ No newline at end of file +var foo:(a:{():number; (i:number):number; })=>number; foo = function(this: void, a:string){return 5}; \ No newline at end of file diff --git a/tests/cases/conformance/types/thisType/looseThisTypeInFunctions.ts b/tests/cases/conformance/types/thisType/looseThisTypeInFunctions.ts index 34d1ebd4a8e..3e8bdb11170 100644 --- a/tests/cases/conformance/types/thisType/looseThisTypeInFunctions.ts +++ b/tests/cases/conformance/types/thisType/looseThisTypeInFunctions.ts @@ -1,4 +1,5 @@ interface I { + n: number; explicitThis(this: this, m: number): number; } interface Unused { @@ -19,10 +20,19 @@ class C implements I { let c = new C(); c.explicitVoid = c.explicitThis; // error, 'void' is missing everything let o = { - explicitThis: function (m) { return m }, - implicitThis(m: number): number { return m } + n: 101, + explicitThis: function (m: number) { + return m + this.n.length; // ok, this.n: any + }, + implicitThis(m: number): number { return m; } }; let i: I = o; +let o2: I = { + n: 1001 + explicitThis: function (m) { + return m + this.n.length; // error, this.n: number, no member 'length' + }, +} let x = i.explicitThis; let n = x(12); // callee:void doesn't match this:I let u: Unused; @@ -32,3 +42,6 @@ c.explicitVoid = c.implicitThis // ok, implicitThis(this:any) o.implicitThis = c.implicitThis; // ok, implicitThis(this:any) o.implicitThis = c.explicitThis; // ok, implicitThis(this:any) is assignable to explicitThis(this: this) o.implicitThis = i.explicitThis; +i.explicitThis = function(m) { + return this.n.length; // error, this.n: number +} diff --git a/tests/cases/conformance/types/thisType/thisTypeInFuncTemp.ts b/tests/cases/conformance/types/thisType/thisTypeInFuncTemp.ts new file mode 100644 index 00000000000..2f4873031fb --- /dev/null +++ b/tests/cases/conformance/types/thisType/thisTypeInFuncTemp.ts @@ -0,0 +1,27 @@ +// @strictThis: true +// 1. contextual typing predicate is wrong (currently: method2: function () ...) +// () -> yes (allParametersAreUntyped=t, noThisParameter=t, noTypeParameters=t) +// ok .. fixed? +// 2. contextual typing of this doesn't seem to work +// strictThis was turned off. DUH. +// 3. when it DID work, it was giving bogus types with strictThis OFF (see the last example) +interface T { + (x: number): void; +} +interface I { + n: number + method(this: this): number; + method2(this: this): number; +} +let i: I = { + n: 12, + method: function(this) { // this: I + return this.n.length; // error, 'number' has no property 'length' + }, + method2: function() { // this: I + return this.n.length; // error, 'number' has no property 'length' + } +} +i.method = function () { return this.n.length } // this: I +i.method = function (this) { return this.n.length } // this: I +var t: T = function (this, y) { } // yes! (but this: any NOT number!!) \ No newline at end of file diff --git a/tests/cases/conformance/types/thisType/thisTypeInFunctions.ts b/tests/cases/conformance/types/thisType/thisTypeInFunctions.ts index 623c880d339..f92b8ab4e15 100644 --- a/tests/cases/conformance/types/thisType/thisTypeInFunctions.ts +++ b/tests/cases/conformance/types/thisType/thisTypeInFunctions.ts @@ -140,10 +140,10 @@ c.explicitThis = explicitCFunction; c.explicitThis = function(this: C, m: number) { return this.n + m }; // this:any compatibility -c.explicitC = function(m: number) { return this.n + m }; -c.explicitProperty = function(m: number) { return this.n + m }; -c.explicitThis = function(m: number) { return this.n + m }; -c.implicitThis = function(m: number) { return this.n + m }; +c.explicitC = function(m) { return this.n + m }; +c.explicitProperty = function(m) { return this.n + m }; +c.explicitThis = function(m) { return this.n + m }; +c.implicitThis = function(m) { return this.n + m }; c.implicitThis = reconstructed.implicitThis; c.explicitC = function(this: B, m: number) { return this.n + m };