Skip to content

Make an error message related to the PropertyDecorator kinder #61637

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 34 additions & 33 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36109,7 +36109,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return constructorSymbol === globalPromiseSymbol;
}

function getArgumentArityError(node: CallLikeExpression, signatures: readonly Signature[], args: readonly Expression[], headMessage?: DiagnosticMessage) {
function getArgumentArityError(node: CallLikeExpression, signatures: readonly Signature[], args: readonly Expression[], headMessageAndArgs?: DiagnosticAndArguments) {
const spreadIndex = getSpreadArgumentIndex(args);
if (spreadIndex > -1) {
return createDiagnosticForNode(args[spreadIndex], Diagnostics.A_spread_argument_must_either_have_a_tuple_type_or_be_passed_to_a_rest_parameter);
Expand Down Expand Up @@ -36150,19 +36150,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {

if (min < args.length && args.length < max) {
// between min and max, but with no matching overload
if (headMessage) {
if (headMessageAndArgs) {
let chain = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.No_overload_expects_0_arguments_but_overloads_do_exist_that_expect_either_1_or_2_arguments, args.length, maxBelow, minAbove);
chain = chainDiagnosticMessages(chain, headMessage);
chain = chainDiagnosticMessages(chain, ...headMessageAndArgs);
return getDiagnosticForCallNode(node, chain);
}
return getDiagnosticForCallNode(node, Diagnostics.No_overload_expects_0_arguments_but_overloads_do_exist_that_expect_either_1_or_2_arguments, args.length, maxBelow, minAbove);
}
else if (args.length < min) {
// too short: put the error span on the call expression, not any of the args
let diagnostic: Diagnostic;
if (headMessage) {
if (headMessageAndArgs) {
let chain = chainDiagnosticMessages(/*details*/ undefined, error, parameterRange, args.length);
chain = chainDiagnosticMessages(chain, headMessage);
chain = chainDiagnosticMessages(chain, ...headMessageAndArgs);
diagnostic = getDiagnosticForCallNode(node, chain);
}
else {
Expand All @@ -36187,25 +36187,25 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
end++;
}
setTextRangePosEnd(errorSpan, pos, end);
if (headMessage) {
if (headMessageAndArgs) {
let chain = chainDiagnosticMessages(/*details*/ undefined, error, parameterRange, args.length);
chain = chainDiagnosticMessages(chain, headMessage);
chain = chainDiagnosticMessages(chain, ...headMessageAndArgs);
return createDiagnosticForNodeArrayFromMessageChain(getSourceFileOfNode(node), errorSpan, chain);
}
return createDiagnosticForNodeArray(getSourceFileOfNode(node), errorSpan, error, parameterRange, args.length);
}
}

function getTypeArgumentArityError(node: Node, signatures: readonly Signature[], typeArguments: NodeArray<TypeNode>, headMessage?: DiagnosticMessage) {
function getTypeArgumentArityError(node: Node, signatures: readonly Signature[], typeArguments: NodeArray<TypeNode>, headMessageAndArgs?: DiagnosticAndArguments) {
const argCount = typeArguments.length;
// No overloads exist
if (signatures.length === 1) {
const sig = signatures[0];
const min = getMinTypeArgumentCount(sig.typeParameters);
const max = length(sig.typeParameters);
if (headMessage) {
if (headMessageAndArgs) {
let chain = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Expected_0_type_arguments_but_got_1, min < max ? min + "-" + max : min, argCount);
chain = chainDiagnosticMessages(chain, headMessage);
chain = chainDiagnosticMessages(chain, ...headMessageAndArgs);
return createDiagnosticForNodeArrayFromMessageChain(getSourceFileOfNode(node), typeArguments, chain);
}
return createDiagnosticForNodeArray(getSourceFileOfNode(node), typeArguments, Diagnostics.Expected_0_type_arguments_but_got_1, min < max ? min + "-" + max : min, argCount);
Expand All @@ -36224,22 +36224,22 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
if (belowArgCount !== -Infinity && aboveArgCount !== Infinity) {
if (headMessage) {
if (headMessageAndArgs) {
let chain = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.No_overload_expects_0_type_arguments_but_overloads_do_exist_that_expect_either_1_or_2_type_arguments, argCount, belowArgCount, aboveArgCount);
chain = chainDiagnosticMessages(chain, headMessage);
chain = chainDiagnosticMessages(chain, ...headMessageAndArgs);
return createDiagnosticForNodeArrayFromMessageChain(getSourceFileOfNode(node), typeArguments, chain);
}
return createDiagnosticForNodeArray(getSourceFileOfNode(node), typeArguments, Diagnostics.No_overload_expects_0_type_arguments_but_overloads_do_exist_that_expect_either_1_or_2_type_arguments, argCount, belowArgCount, aboveArgCount);
}
if (headMessage) {
if (headMessageAndArgs) {
let chain = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Expected_0_type_arguments_but_got_1, belowArgCount === -Infinity ? aboveArgCount : belowArgCount, argCount);
chain = chainDiagnosticMessages(chain, headMessage);
chain = chainDiagnosticMessages(chain, ...headMessageAndArgs);
return createDiagnosticForNodeArrayFromMessageChain(getSourceFileOfNode(node), typeArguments, chain);
}
return createDiagnosticForNodeArray(getSourceFileOfNode(node), typeArguments, Diagnostics.Expected_0_type_arguments_but_got_1, belowArgCount === -Infinity ? aboveArgCount : belowArgCount, argCount);
}

function resolveCall(node: CallLikeExpression, signatures: readonly Signature[], candidatesOutArray: Signature[] | undefined, checkMode: CheckMode, callChainFlags: SignatureFlags, headMessage?: DiagnosticMessage): Signature {
function resolveCall(node: CallLikeExpression, signatures: readonly Signature[], candidatesOutArray: Signature[] | undefined, checkMode: CheckMode, callChainFlags: SignatureFlags, headMessageAndArgs?: DiagnosticAndArguments): Signature {
const isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression;
const isDecorator = node.kind === SyntaxKind.Decorator;
const isJsxOpeningOrSelfClosingElement = isJsxOpeningLikeElement(node);
Expand Down Expand Up @@ -36370,8 +36370,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// If the call expression is a synthetic call to a `[Symbol.hasInstance]` method then we will produce a head
// message when reporting diagnostics that explains how we got to `right[Symbol.hasInstance](left)` from
// `left instanceof right`, as it pertains to "Argument" related messages reported for the call.
if (!headMessage && isInstanceof) {
headMessage = Diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_assignable_to_the_first_argument_of_the_right_hand_side_s_Symbol_hasInstance_method;
if (!headMessageAndArgs && isInstanceof) {
headMessageAndArgs = [Diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_assignable_to_the_first_argument_of_the_right_hand_side_s_Symbol_hasInstance_method];
}
if (candidatesForArgumentError) {
if (candidatesForArgumentError.length === 1 || candidatesForArgumentError.length > 3) {
Expand All @@ -36381,8 +36381,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
chain = chainDiagnosticMessages(chain, Diagnostics.The_last_overload_gave_the_following_error);
chain = chainDiagnosticMessages(chain, Diagnostics.No_overload_matches_this_call);
}
if (headMessage) {
chain = chainDiagnosticMessages(chain, headMessage);
if (headMessageAndArgs) {
chain = chainDiagnosticMessages(chain, ...headMessageAndArgs);
}
const diags = getSignatureApplicabilityError(node, args, last, assignableRelation, CheckMode.Normal, /*reportErrors*/ true, () => chain, /*inferenceContext*/ undefined);
if (diags) {
Expand Down Expand Up @@ -36427,8 +36427,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
map(diags, createDiagnosticMessageChainFromDiagnostic),
Diagnostics.No_overload_matches_this_call,
);
if (headMessage) {
chain = chainDiagnosticMessages(chain, headMessage);
if (headMessageAndArgs) {
chain = chainDiagnosticMessages(chain, ...headMessageAndArgs);
}
// The below is a spread to guarantee we get a new (mutable) array - our `flatMap` helper tries to do "smart" optimizations where it reuses input
// arrays and the emptyArray singleton where possible, which is decidedly not what we want while we're still constructing this diagnostic
Expand All @@ -36446,18 +36446,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
else if (candidateForArgumentArityError) {
diagnostics.add(getArgumentArityError(node, [candidateForArgumentArityError], args, headMessage));
diagnostics.add(getArgumentArityError(node, [candidateForArgumentArityError], args, headMessageAndArgs));
}
else if (candidateForTypeArgumentError) {
const [headMessage] = headMessageAndArgs ?? [];
checkTypeArguments(candidateForTypeArgumentError, (node as CallExpression | TaggedTemplateExpression | JsxOpeningLikeElement).typeArguments!, /*reportErrors*/ true, headMessage);
}
else if (!isJsxOpenFragment) {
const signaturesWithCorrectTypeArgumentArity = filter(signatures, s => hasCorrectTypeArgumentArity(s, typeArguments));
if (signaturesWithCorrectTypeArgumentArity.length === 0) {
diagnostics.add(getTypeArgumentArityError(node, signatures, typeArguments!, headMessage));
diagnostics.add(getTypeArgumentArityError(node, signatures, typeArguments!, headMessageAndArgs));
}
else {
diagnostics.add(getArgumentArityError(node, signaturesWithCorrectTypeArgumentArity, args, headMessage));
diagnostics.add(getArgumentArityError(node, signaturesWithCorrectTypeArgumentArity, args, headMessageAndArgs));
}
}
}
Expand Down Expand Up @@ -37131,24 +37132,24 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}

/**
* Gets the localized diagnostic head message to use for errors when resolving a decorator as a call expression.
* Gets the localized diagnostic head message and its arguments to use for errors when resolving a decorator as a call expression.
*/
function getDiagnosticHeadMessageForDecoratorResolution(node: Decorator) {
function getDiagnosticHeadMessageAndArgsForDecoratorResolution(node: Decorator): DiagnosticAndArguments {
switch (node.parent.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
return Diagnostics.Unable_to_resolve_signature_of_class_decorator_when_called_as_an_expression;
return [Diagnostics.Unable_to_resolve_signature_of_class_decorator_when_called_as_an_expression];

case SyntaxKind.Parameter:
return Diagnostics.Unable_to_resolve_signature_of_parameter_decorator_when_called_as_an_expression;
return [Diagnostics.Unable_to_resolve_signature_of_parameter_decorator_when_called_as_an_expression];

case SyntaxKind.PropertyDeclaration:
return Diagnostics.Unable_to_resolve_signature_of_property_decorator_when_called_as_an_expression;
return [Diagnostics.Expression_type_is_not_assignable_to_decorator_type_PropertyDecorator_Ensure_0_has_a_type_assignable_to_PropertyDecorator, getTextOfNode(node)];

case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return Diagnostics.Unable_to_resolve_signature_of_method_decorator_when_called_as_an_expression;
return [Diagnostics.Unable_to_resolve_signature_of_method_decorator_when_called_as_an_expression];

default:
return Debug.fail();
Expand Down Expand Up @@ -37177,10 +37178,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return resolveErrorCall(node);
}

const headMessage = getDiagnosticHeadMessageForDecoratorResolution(node);
const headMessageAndArgs = getDiagnosticHeadMessageAndArgsForDecoratorResolution(node);
if (!callSignatures.length) {
const errorDetails = invocationErrorDetails(node.expression, apparentType, SignatureKind.Call);
const messageChain = chainDiagnosticMessages(errorDetails.messageChain, headMessage);
const messageChain = chainDiagnosticMessages(errorDetails.messageChain, ...headMessageAndArgs);
const diag = createDiagnosticForNodeFromMessageChain(getSourceFileOfNode(node.expression), node.expression, messageChain);
if (errorDetails.relatedMessage) {
addRelatedInfo(diag, createDiagnosticForNode(node.expression, errorDetails.relatedMessage));
Expand All @@ -37190,7 +37191,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return resolveErrorCall(node);
}

return resolveCall(node, callSignatures, candidatesOutArray, checkMode, SignatureFlags.None, headMessage);
return resolveCall(node, callSignatures, candidatesOutArray, checkMode, SignatureFlags.None, headMessageAndArgs);
}

function createSignatureForJSXIntrinsic(node: JsxCallLike, result: Type): Signature {
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,7 @@
"category": "Error",
"code": 1239
},
"Unable to resolve signature of property decorator when called as an expression.": {
"Expression type is not assignable to decorator type 'PropertyDecorator'. Ensure '{0}' has a type assignable to 'PropertyDecorator'.": {
"category": "Error",
"code": 1240
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
decoratorOnClassProperty6.ts(4,6): error TS1240: Unable to resolve signature of property decorator when called as an expression.
decoratorOnClassProperty6.ts(4,6): error TS1240: Expression type is not assignable to decorator type 'PropertyDecorator'. Ensure '@dec' has a type assignable to 'PropertyDecorator'.
The runtime will invoke the decorator with 2 arguments, but the decorator expects 1.


Expand All @@ -8,6 +8,6 @@ decoratorOnClassProperty6.ts(4,6): error TS1240: Unable to resolve signature of
class C {
@dec prop;
~~~
!!! error TS1240: Unable to resolve signature of property decorator when called as an expression.
!!! error TS1240: Expression type is not assignable to decorator type 'PropertyDecorator'. Ensure '@dec' has a type assignable to 'PropertyDecorator'.
!!! error TS1240: The runtime will invoke the decorator with 2 arguments, but the decorator expects 1.
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
decoratorOnClassProperty7.ts(4,5): error TS1240: Unable to resolve signature of property decorator when called as an expression.
decoratorOnClassProperty7.ts(4,5): error TS1240: Expression type is not assignable to decorator type 'PropertyDecorator'. Ensure '@dec' has a type assignable to 'PropertyDecorator'.
The runtime will invoke the decorator with 2 arguments, but the decorator expects 3.


Expand All @@ -8,7 +8,7 @@ decoratorOnClassProperty7.ts(4,5): error TS1240: Unable to resolve signature of
class C {
@dec prop;
~~~~
!!! error TS1240: Unable to resolve signature of property decorator when called as an expression.
!!! error TS1240: Expression type is not assignable to decorator type 'PropertyDecorator'. Ensure '@dec' has a type assignable to 'PropertyDecorator'.
!!! error TS1240: The runtime will invoke the decorator with 2 arguments, but the decorator expects 3.
!!! related TS6210 decoratorOnClassProperty7.ts:1:70: An argument for 'paramIndex' was not provided.
}