diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e5a57cd96a9..4ce7f6cd0a8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5465,21 +5465,17 @@ namespace ts { target = getErasedSignature(target); let result = Ternary.True; - if (source.thisType && target.thisType) { - if (source.thisType !== voidType) { - // void sources are assignable to anything. - let related = compareTypes(target.thisType, source.thisType, reportErrors); - if (!related) { - related = compareTypes(source.thisType, target.thisType, /*reportErrors*/ false); - if (!related) { - if (reportErrors) { - errorReporter(Diagnostics.Types_of_parameters_0_and_1_are_incompatible, "this", "this"); - } - return Ternary.False; - } + if (source.thisType && target.thisType && source.thisType !== voidType) { + // void sources are assignable to anything. + const related = compareTypes(source.thisType, target.thisType, /*reportErrors*/ false) + || compareTypes(target.thisType, source.thisType, reportErrors); + if (!related) { + if (reportErrors) { + errorReporter(Diagnostics.Types_of_parameters_0_and_1_are_incompatible, "this", "this"); } - result &= related; + return Ternary.False; } + result &= related; } const sourceMax = getNumNonRestParameters(source); @@ -10199,18 +10195,20 @@ namespace ts { } function checkApplicableSignature(node: CallLikeExpression, args: Expression[], signature: Signature, relation: Map, excludeArgument: boolean[], reportErrors: boolean) { - const headMessage = Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1; + if (signature.thisType && signature.thisType !== voidType && node.kind !== SyntaxKind.NewExpression) { - // If the source's this is not of the form `x.f` or `x[f]`, then sourceType = voidType - // If the target's this is voidType, then the check is skipped -- anything is compatible. + // If the called expression is not of the form `x.f` or `x["f"]`, then sourceType = voidType + // If the signature's 'this' type is voidType, then the check is skipped -- anything is compatible. // If the expression is a new expression, then the check is skipped. const thisArgumentNode = getThisArgumentOfCall(node); const thisArgumentType = thisArgumentNode ? checkExpression(thisArgumentNode) : voidType; const errorNode = reportErrors ? (thisArgumentNode || node) : undefined; + const headMessage = Diagnostics.this_context_of_type_0_is_not_assignable_to_method_this_of_type_1; if (!checkTypeRelatedTo(thisArgumentType, signature.thisType, relation, errorNode, headMessage)) { return false; } } + const headMessage = Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1; const argCount = getEffectiveArgumentCount(node, args, signature); for (let i = 0; i < argCount; i++) { const arg = getEffectiveArgument(node, args, i); @@ -12396,7 +12394,7 @@ namespace ts { } if ((node.name).text === "this") { if (indexOf(func.parameters, node) !== 0) { - error(node, Diagnostics.this_parameter_must_be_the_first_parameter); + error(node, Diagnostics.A_this_parameter_must_be_the_first_parameter); } if (func.kind === SyntaxKind.Constructor || func.kind === SyntaxKind.ConstructSignature) { error(node, Diagnostics.A_constructor_cannot_have_a_this_parameter); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 0da7cb72932..1f282b10fd8 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1879,7 +1879,7 @@ "category": "Error", "code": 2678 }, - "'this' parameter must be the first parameter.": { + "A 'this' parameter must be the first parameter.": { "category": "Error", "code": 2679 }, @@ -1895,6 +1895,10 @@ "category": "Error", "code": 2682 }, + "'this' context of type '{0}' is not assignable to method 'this' of type '{1}'.": { + "category": "Error", + "code": 2683 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", "code": 4000 diff --git a/tests/baselines/reference/looseThisTypeInFunctions.errors.txt b/tests/baselines/reference/looseThisTypeInFunctions.errors.txt index caaf7afb09c..4531797586c 100644 --- a/tests/baselines/reference/looseThisTypeInFunctions.errors.txt +++ b/tests/baselines/reference/looseThisTypeInFunctions.errors.txt @@ -2,7 +2,7 @@ tests/cases/conformance/types/thisType/looseThisTypeInFunctions.ts(21,1): error Types of parameters 'this' and 'this' are incompatible. Type 'void' is not assignable to type 'C'. tests/cases/conformance/types/thisType/looseThisTypeInFunctions.ts(33,28): 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(37,9): error TS2683: 'this' context of type 'void' is not assignable to method 'this' of type 'I'. tests/cases/conformance/types/thisType/looseThisTypeInFunctions.ts(46,20): error TS2339: Property 'length' does not exist on type 'number'. @@ -51,7 +51,7 @@ tests/cases/conformance/types/thisType/looseThisTypeInFunctions.ts(46,20): error let x = i.explicitThis; let n = x(12); // callee:void doesn't match this:I ~~~~~ -!!! error TS2345: Argument of type 'void' is not assignable to parameter of type 'I'. +!!! error TS2683: 'this' context of type 'void' is not assignable to method 'this' of type 'I'. let u: Unused; let y = u.implicitNoThis; n = y(12); // ok, callee:void matches this:any diff --git a/tests/baselines/reference/thisTypeInFunctionsNegative.errors.txt b/tests/baselines/reference/thisTypeInFunctionsNegative.errors.txt index 305208ac7d7..3c25b31e0a8 100644 --- a/tests/baselines/reference/thisTypeInFunctionsNegative.errors.txt +++ b/tests/baselines/reference/thisTypeInFunctionsNegative.errors.txt @@ -1,7 +1,7 @@ tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(16,15): error TS2339: Property 'n' does not exist on type 'void'. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(39,21): error TS2339: Property 'a' does not exist on type 'void'. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(49,1): error TS2345: Argument of type 'void' is not assignable to parameter of type '{ a: number; }'. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(51,1): error TS2345: Argument of type 'void' is not assignable to parameter of type 'I'. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(49,1): error TS2683: 'this' context of type 'void' is not assignable to method 'this' of type '{ a: number; }'. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(51,1): error TS2683: 'this' context of type 'void' is not assignable to method 'this' of type 'I'. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(56,21): error TS2339: Property 'notFound' does not exist on type '{ y: number; }'. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(59,21): error TS2339: Property 'notSpecified' does not exist on type 'void'. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(61,79): error TS2322: Type '{ y: number; explicitStructural: (this: { y: number; }, x: number) => number; }' is not assignable to type '{ y: number; f: (this: { y: number; }, x: number) => number; }'. @@ -13,10 +13,10 @@ tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(63,110): e tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(65,1): error TS2346: Supplied parameters do not match any signature of call target. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(66,6): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(67,1): error TS2346: Supplied parameters do not match any signature of call target. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(68,1): error TS2345: Argument of type '{ y: string; f: (this: { y: number; }, x: number) => number; }' is not assignable to parameter of type '{ y: number; }'. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(68,1): error TS2683: 'this' context of type '{ y: string; f: (this: { y: number; }, x: number) => number; }' is not assignable to method 'this' of type '{ y: number; }'. Types of property 'y' are incompatible. Type 'string' is not assignable to type 'number'. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(69,1): error TS2345: Argument of type '{ wrongName: number; f: (this: { y: number; }, x: number) => number; }' is not assignable to parameter of type '{ y: number; }'. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(69,1): error TS2683: 'this' context of type '{ wrongName: number; f: (this: { y: number; }, x: number) => number; }' is not assignable to method 'this' of type '{ y: number; }'. Property 'y' is missing in type '{ wrongName: number; f: (this: { y: number; }, x: number) => number; }'. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(72,1): error TS2346: Supplied parameters do not match any signature of call target. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(73,13): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. @@ -77,7 +77,7 @@ tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(154,16): e tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(158,17): error TS2680: A constructor cannot have a 'this' parameter. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(160,11): error TS2681: A setter cannot have a 'this' parameter. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(164,9): error TS2680: A constructor cannot have a 'this' parameter. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(166,30): error TS2679: 'this' parameter must be the first parameter. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(166,30): error TS2679: A 'this' parameter must be the first parameter. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(169,26): error TS1003: Identifier expected. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(169,30): error TS1005: ',' expected. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(170,20): error TS2370: A rest parameter must be of an array type. @@ -154,11 +154,11 @@ tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(176,35): e let implExplicitStructural = impl.explicitStructural; implExplicitStructural(); // error, no 'a' in 'void' ~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2345: Argument of type 'void' is not assignable to parameter of type '{ a: number; }'. +!!! error TS2683: 'this' context of type 'void' is not assignable to method 'this' of type '{ a: number; }'. let implExplicitInterface = impl.explicitInterface; implExplicitInterface(); // error, no 'a' in 'void' ~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2345: Argument of type 'void' is not assignable to parameter of type 'I'. +!!! error TS2683: 'this' context of type 'void' is not assignable to method 'this' of type 'I'. function explicitStructural(this: { y: number }, x: number): number { return x + this.y; } @@ -196,13 +196,13 @@ tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(176,35): e !!! error TS2346: Supplied parameters do not match any signature of call target. wrongPropertyType.f(13); ~~~~~~~~~~~~~~~~~ -!!! error TS2345: Argument of type '{ y: string; f: (this: { y: number; }, x: number) => number; }' is not assignable to parameter of type '{ y: number; }'. -!!! error TS2345: Types of property 'y' are incompatible. -!!! error TS2345: Type 'string' is not assignable to type 'number'. +!!! error TS2683: 'this' context of type '{ y: string; f: (this: { y: number; }, x: number) => number; }' is not assignable to method 'this' of type '{ y: number; }'. +!!! error TS2683: Types of property 'y' are incompatible. +!!! error TS2683: Type 'string' is not assignable to type 'number'. wrongPropertyName.f(13); ~~~~~~~~~~~~~~~~~ -!!! error TS2345: Argument of type '{ wrongName: number; f: (this: { y: number; }, x: number) => number; }' is not assignable to parameter of type '{ y: number; }'. -!!! error TS2345: Property 'y' is missing in type '{ wrongName: number; f: (this: { y: number; }, x: number) => number; }'. +!!! error TS2683: 'this' context of type '{ wrongName: number; f: (this: { y: number; }, x: number) => number; }' is not assignable to method 'this' of type '{ y: number; }'. +!!! error TS2683: Property 'y' is missing in type '{ wrongName: number; f: (this: { y: number; }, x: number) => number; }'. let c = new C(); c.explicitC(); // not enough arguments @@ -391,7 +391,7 @@ tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(176,35): e } function notFirst(a: number, this: C): number { return this.n; } ~~~~~~~ -!!! error TS2679: 'this' parameter must be the first parameter. +!!! error TS2679: A 'this' parameter must be the first parameter. ///// parse errors ///// function modifiers(async this: C): number { return this.n; }