diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e99dd7eeb4e..6cd98c43529 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6774,13 +6774,29 @@ module ts { let arg = args[i]; if (arg.kind !== SyntaxKind.OmittedExpression) { let paramType = getTypeAtPosition(signature, i); - let argType = getSyntheticArgumentType(i, arg); + let argType: Type; + if (arg.parent.kind === SyntaxKind.Decorator) { + if (i === 0) { + argType = getDecoratorTargetArgumentType(arg.parent.parent); + } + else if (i === 1) { + argType = getDecoratorPropertyKeyArgumentType(arg.parent.parent); + } + else if (i === 2) { + argType = getDecoratorPropertyIndexOrDescriptorArgumentType(arg.parent.parent); + } + } + else if (i === 0 && arg.parent.kind === SyntaxKind.TaggedTemplateExpression) { + argType = globalTemplateStringsArrayType; + } + if (argType === undefined) { // For context sensitive arguments we pass the identityMapper, which is a signal to treat all // context sensitive function expressions as wildcards let mapper = excludeArgument && excludeArgument[i] !== undefined ? identityMapper : inferenceMapper; argType = checkExpressionWithContextualType(arg, paramType, mapper); } + inferTypes(context, argType, paramType); } } @@ -6838,140 +6854,151 @@ module ts { : emptyObjectType; } - /** - * Gets the type for a synthetic argument when resolving the first argument for a TaggedTemplateExpression - * or any arguments to a Decorator. - */ - function getSyntheticArgumentType(argumentIndex: number, arg: Expression): Type { - if (arg.parent.kind === SyntaxKind.Decorator) { - let decorator = arg.parent; - let parent = decorator.parent; - if (argumentIndex === 0) { - // The first argument to a decorator is its `target`. - switch (parent.kind) { - case SyntaxKind.ClassDeclaration: - // For a class decorator, the `target` is the type of the class (e.g. the - // "static" or "constructor" side of the class) - let classSymbol = getSymbolOfNode(parent); - return getTypeOfSymbol(classSymbol); - - case SyntaxKind.Parameter: - // For a parameter decorator, the `target` is the parent type of the - // parameter's containing method. - parent = parent.parent; - if (parent.kind === SyntaxKind.Constructor) { - let classSymbol = getSymbolOfNode(parent); - return getTypeOfSymbol(classSymbol); - } - - // fall-through - - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - // For a property or method decorator, the `target` is the - // "static"-side type of the parent of the member if the member is - // declared "static"; otherwise, it is the "instance"-side type of the - // parent of the member. - return getTypeOfParentOfClassElement(parent); + function getDecoratorTargetArgumentType(target: Node): Type { + // The first argument to a decorator is its `target`. + switch (target.kind) { + case SyntaxKind.ClassDeclaration: + // For a class decorator, the `target` is the type of the class (e.g. the + // "static" or "constructor" side of the class) + let classSymbol = getSymbolOfNode(target); + return getTypeOfSymbol(classSymbol); + + case SyntaxKind.Parameter: + // For a parameter decorator, the `target` is the parent type of the + // parameter's containing method. + target = target.parent; + if (target.kind === SyntaxKind.Constructor) { + let classSymbol = getSymbolOfNode(target); + return getTypeOfSymbol(classSymbol); } - } - else if (argumentIndex === 1) { - // The second argument to a decorator is its `propertyKey` - switch (parent.kind) { - case SyntaxKind.ClassDeclaration: - Debug.fail("Class decorators should not have a second synthetic argument."); - - case SyntaxKind.Parameter: - parent = parent.parent; - if (parent.kind === SyntaxKind.Constructor) { - // For a constructor parameter decorator, the `propertyKey` will be `undefined`. - return anyType; - } - - // For a non-constructor parameter decorator, the `propertyKey` will be either - // a string or a symbol, based on the name of the parameter's containing method. - - // fall-through - - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - // The `propertyKey` for a property or method decorator will be a - // string literal type if the member name is an identifier, number, or string; - // otherwise, if the member name is a computed property name it will - // be either string or symbol. - let element = decorator.parent; - switch (element.name.kind) { - case SyntaxKind.Identifier: - case SyntaxKind.NumericLiteral: - case SyntaxKind.StringLiteral: - return getStringLiteralType(element.name); - - case SyntaxKind.ComputedPropertyName: - let nameType = checkComputedPropertyName(element.name); - if (allConstituentTypesHaveKind(nameType, TypeFlags.ESSymbol)) { - return nameType; - } - else { - return stringType; - } - } - } - } - else if (argumentIndex === 2) { - // The third argument to a decorator is either its `descriptor` for a method decorator - // or its `parameterIndex` for a paramter decorator - switch (parent.kind) { - case SyntaxKind.ClassDeclaration: - Debug.fail("Class decorators should not have a third synthetic argument."); - break; - - case SyntaxKind.Parameter: - // The `parameterIndex` for a parameter decorator is always a number - return numberType; - - case SyntaxKind.PropertyDeclaration: - Debug.fail("Property decorators should not have a third synthetic argument."); - break; - - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - // The `descriptor` for a method decorator will be a `TypedPropertyDescriptor` - // for the type of the member. - let propertyType: Type = getTypeOfNode(parent); - return createTypedPropertyDescriptorType(propertyType); - } - } + + // fall-through + + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + // For a property or method decorator, the `target` is the + // "static"-side type of the parent of the member if the member is + // declared "static"; otherwise, it is the "instance"-side type of the + // parent of the member. + return getTypeOfParentOfClassElement(target); + + default: + return undefined; } - else if (argumentIndex === 0 && arg.parent.kind === SyntaxKind.TaggedTemplateExpression) { - return globalTemplateStringsArrayType; - } - - return undefined; } + + function getDecoratorPropertyKeyArgumentType(target: Node) { + // The second argument to a decorator is its `propertyKey` + switch (target.kind) { + case SyntaxKind.ClassDeclaration: + Debug.fail("Class decorators should not have a second synthetic argument."); + + case SyntaxKind.Parameter: + target = target.parent; + if (target.kind === SyntaxKind.Constructor) { + // For a constructor parameter decorator, the `propertyKey` will be `undefined`. + return anyType; + } + + // For a non-constructor parameter decorator, the `propertyKey` will be either + // a string or a symbol, based on the name of the parameter's containing method. + + // fall-through + + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + // The `propertyKey` for a property or method decorator will be a + // string literal type if the member name is an identifier, number, or string; + // otherwise, if the member name is a computed property name it will + // be either string or symbol. + let element = target; + switch (element.name.kind) { + case SyntaxKind.Identifier: + case SyntaxKind.NumericLiteral: + case SyntaxKind.StringLiteral: + return getStringLiteralType(element.name); + + case SyntaxKind.ComputedPropertyName: + let nameType = checkComputedPropertyName(element.name); + if (allConstituentTypesHaveKind(nameType, TypeFlags.ESSymbol)) { + return nameType; + } + else { + return stringType; + } + } + } + } + + function getDecoratorPropertyIndexOrDescriptorArgumentType(parent: Node) { + // The third argument to a decorator is either its `descriptor` for a method decorator + // or its `parameterIndex` for a paramter decorator + switch (parent.kind) { + case SyntaxKind.ClassDeclaration: + Debug.fail("Class decorators should not have a third synthetic argument."); + break; + case SyntaxKind.Parameter: + // The `parameterIndex` for a parameter decorator is always a number + return numberType; + + case SyntaxKind.PropertyDeclaration: + Debug.fail("Property decorators should not have a third synthetic argument."); + break; + + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + // The `descriptor` for a method decorator will be a `TypedPropertyDescriptor` + // for the type of the member. + let propertyType: Type = getTypeOfNode(parent); + return createTypedPropertyDescriptorType(propertyType); + } + } + function checkApplicableSignature(node: CallLikeExpression, args: Expression[], signature: Signature, relation: Map, excludeArgument: boolean[], reportErrors: boolean, containingMessageChain?: DiagnosticMessageChain) { for (let i = 0; i < args.length; i++) { let arg = args[i]; if (arg.kind !== SyntaxKind.OmittedExpression) { // Check spread elements against rest type (from arity check we know spread argument corresponds to a rest parameter) let paramType = getTypeAtPosition(signature, i); - // A tagged template expression provides a special first argument, and string literals get string literal types + + // Decorators provide special arguments, a tagged template expression provides + // a special first argument, and string literals get string literal types // unless we're reporting errors - let argType = getSyntheticArgumentType(i, arg); + let argType: Type; + if (arg.parent.kind === SyntaxKind.Decorator) { + if (i === 0) { + argType = getDecoratorTargetArgumentType(arg.parent.parent); + } + else if (i === 1) { + argType = getDecoratorPropertyKeyArgumentType(arg.parent.parent); + } + else if (i === 2) { + argType = getDecoratorPropertyIndexOrDescriptorArgumentType(arg.parent.parent); + } + } + else if (i === 0 && arg.parent.kind === SyntaxKind.TaggedTemplateExpression) { + argType = globalTemplateStringsArrayType; + } + if (argType === undefined) { - argType = arg.kind === SyntaxKind.StringLiteral && !reportErrors - ? getStringLiteralType(arg) - : checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined); + if (arg.kind === SyntaxKind.StringLiteral && !reportErrors) { + argType = getStringLiteralType(arg); + } + else { + argType = checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined); + } } // Use argument expression as error location when reporting errors - if (!checkTypeRelatedTo(argType, paramType, relation, reportErrors ? arg : undefined, - Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1, containingMessageChain)) { + let errorNode = reportErrors ? arg : undefined; + let headMessage = Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1; + if (!checkTypeRelatedTo(argType, paramType, relation, errorNode, headMessage, containingMessageChain)) { return false; } } @@ -6986,7 +7013,8 @@ module ts { * If 'node' is a TaggedTemplateExpression, a new argument list is constructed from the substitution * expressions, where the first element of the list is the template for error reporting purposes. * If 'node' is a Decorator, a new argument list is constructed with the decorator - * expression as a placeholder. + * expression as a placeholder to help when choosing an applicable signature and + * for error reporting purposes. */ function getEffectiveCallArguments(node: CallLikeExpression): Expression[] { let args: Expression[]; @@ -7082,6 +7110,9 @@ module ts { // // For a tagged template, then the first argument be 'undefined' if necessary // because it represents a TemplateStringsArray. + // + // For a decorator, no arguments are susceptible to contextual typing due to the fact + // decorators are applied to a declaration by the emitter, and not to an expression. let excludeArgument: boolean[]; if (!isDecorator) { for (let i = isTaggedTemplate ? 1 : 0; i < args.length; i++) { @@ -7441,7 +7472,9 @@ module ts { break; } - let diagnosticChainHead = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Invalid_expression_for_0_decorator, decoratorKind); + let diagnosticChainHead = chainDiagnosticMessages( + /*details*/ undefined, + Diagnostics.Unable_to_resolve_signature_of_0_decorator_when_called_as_an_expression, decoratorKind); if (!callSignatures.length) { let errorInfo = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature); errorInfo = concatenateDiagnosticMessageChains(diagnosticChainHead, errorInfo); @@ -9140,7 +9173,7 @@ module ts { diagnosticChainHead = chainDiagnosticMessages( diagnosticChainHead, - Diagnostics.Invalid_expression_for_0_decorator, + Diagnostics.Unable_to_resolve_signature_of_0_decorator_when_called_as_an_expression, decoratorKind); checkTypeAssignableTo(returnType, expectedReturnType, node, /*headMessage*/ undefined, diagnosticChainHead); diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 1eaf69d8886..a1b776611f7 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -175,7 +175,7 @@ module ts { Type_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode: { code: 1216, category: DiagnosticCategory.Error, key: "Type expected. '{0}' is a reserved word in strict mode. Class definitions are automatically in strict mode." }, Export_assignment_is_not_supported_when_module_flag_is_system: { code: 1218, category: DiagnosticCategory.Error, key: "Export assignment is not supported when '--module' flag is 'system'." }, The_return_type_of_a_0_decorator_function_must_be_either_void_or_any: { code: 1219, category: DiagnosticCategory.Error, key: "The return type of a {0} decorator function must be either 'void' or 'any'." }, - Invalid_expression_for_0_decorator: { code: 1220, category: DiagnosticCategory.Error, key: "Invalid expression for {0} decorator." }, + Unable_to_resolve_signature_of_0_decorator_when_called_as_an_expression: { code: 1220, category: DiagnosticCategory.Error, key: "Unable to resolve signature of {0} decorator when called as an expression." }, Duplicate_identifier_0: { code: 2300, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." }, Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor: { code: 2301, category: DiagnosticCategory.Error, key: "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor." }, Static_members_cannot_reference_class_type_parameters: { code: 2302, category: DiagnosticCategory.Error, key: "Static members cannot reference class type parameters." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 36f7505fc65..27238db5d95 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -687,7 +687,7 @@ "category": "Error", "code": 1219 }, - "Invalid expression for {0} decorator.": { + "Unable to resolve signature of {0} decorator when called as an expression.": { "category": "Error", "code": 1220 }, diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index e720c7a112e..f1239b87629 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -620,12 +620,9 @@ module ts { if (node.kind === SyntaxKind.TaggedTemplateExpression) { return (node).tag; } - else if (node.kind === SyntaxKind.Decorator) { - return (node).expression; - } - // Will either be a CallExpression or NewExpression. - return (node).expression; + // Will either be a CallExpression, NewExpression, or Decorator. + return (node).expression; } export function nodeCanBeDecorated(node: Node): boolean { diff --git a/tests/baselines/reference/decoratorOnClass8.errors.txt b/tests/baselines/reference/decoratorOnClass8.errors.txt index 31abd9e2300..b5e27b4b3cb 100644 --- a/tests/baselines/reference/decoratorOnClass8.errors.txt +++ b/tests/baselines/reference/decoratorOnClass8.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/decorators/class/decoratorOnClass8.ts(3,1): error TS1220: Invalid expression for class decorator. +tests/cases/conformance/decorators/class/decoratorOnClass8.ts(3,1): error TS1220: Unable to resolve signature of class decorator when called as an expression. Supplied parameters do not match any signature of call target. @@ -7,7 +7,7 @@ tests/cases/conformance/decorators/class/decoratorOnClass8.ts(3,1): error TS1220 @dec() ~~~~~~ -!!! error TS1220: Invalid expression for class decorator. +!!! error TS1220: Unable to resolve signature of class decorator when called as an expression. !!! error TS1220: Supplied parameters do not match any signature of call target. class C { } \ No newline at end of file diff --git a/tests/baselines/reference/decoratorOnClassMethod10.errors.txt b/tests/baselines/reference/decoratorOnClassMethod10.errors.txt index bfc56717e6c..1d94f08f2a2 100644 --- a/tests/baselines/reference/decoratorOnClassMethod10.errors.txt +++ b/tests/baselines/reference/decoratorOnClassMethod10.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/decorators/class/method/decoratorOnClassMethod10.ts(4,6): error TS1220: Invalid expression for method decorator. +tests/cases/conformance/decorators/class/method/decoratorOnClassMethod10.ts(4,6): error TS1220: Unable to resolve signature of method decorator when called as an expression. Argument of type 'C' is not assignable to parameter of type 'Function'. Property 'apply' is missing in type 'C'. @@ -9,7 +9,7 @@ tests/cases/conformance/decorators/class/method/decoratorOnClassMethod10.ts(4,6) class C { @dec method() {} ~~~ -!!! error TS1220: Invalid expression for method decorator. +!!! error TS1220: Unable to resolve signature of method decorator when called as an expression. !!! error TS1220: Argument of type 'C' is not assignable to parameter of type 'Function'. !!! error TS1220: Property 'apply' is missing in type 'C'. } \ No newline at end of file diff --git a/tests/baselines/reference/decoratorOnClassMethod6.errors.txt b/tests/baselines/reference/decoratorOnClassMethod6.errors.txt index ade416aefbc..2231d0da56d 100644 --- a/tests/baselines/reference/decoratorOnClassMethod6.errors.txt +++ b/tests/baselines/reference/decoratorOnClassMethod6.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/decorators/class/method/decoratorOnClassMethod6.ts(4,5): error TS1220: Invalid expression for method decorator. +tests/cases/conformance/decorators/class/method/decoratorOnClassMethod6.ts(4,5): error TS1220: Unable to resolve signature of method decorator when called as an expression. Supplied parameters do not match any signature of call target. @@ -8,6 +8,6 @@ tests/cases/conformance/decorators/class/method/decoratorOnClassMethod6.ts(4,5): class C { @dec ["method"]() {} ~~~~ -!!! error TS1220: Invalid expression for method decorator. +!!! error TS1220: Unable to resolve signature of method decorator when called as an expression. !!! error TS1220: Supplied parameters do not match any signature of call target. } \ No newline at end of file diff --git a/tests/baselines/reference/decoratorOnClassMethod8.errors.txt b/tests/baselines/reference/decoratorOnClassMethod8.errors.txt index d0635b4f5f3..d80fe7e05f7 100644 --- a/tests/baselines/reference/decoratorOnClassMethod8.errors.txt +++ b/tests/baselines/reference/decoratorOnClassMethod8.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/decorators/class/method/decoratorOnClassMethod8.ts(4,5): error TS1220: Invalid expression for method decorator. +tests/cases/conformance/decorators/class/method/decoratorOnClassMethod8.ts(4,5): error TS1220: Unable to resolve signature of method decorator when called as an expression. Supplied parameters do not match any signature of call target. @@ -8,6 +8,6 @@ tests/cases/conformance/decorators/class/method/decoratorOnClassMethod8.ts(4,5): class C { @dec method() {} ~~~~ -!!! error TS1220: Invalid expression for method decorator. +!!! error TS1220: Unable to resolve signature of method decorator when called as an expression. !!! error TS1220: Supplied parameters do not match any signature of call target. } \ No newline at end of file diff --git a/tests/baselines/reference/decoratorOnClassProperty11.errors.txt b/tests/baselines/reference/decoratorOnClassProperty11.errors.txt index 92b89b0b430..8dad5dc955f 100644 --- a/tests/baselines/reference/decoratorOnClassProperty11.errors.txt +++ b/tests/baselines/reference/decoratorOnClassProperty11.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/decorators/class/property/decoratorOnClassProperty11.ts(4,5): error TS1220: Invalid expression for property decorator. +tests/cases/conformance/decorators/class/property/decoratorOnClassProperty11.ts(4,5): error TS1220: Unable to resolve signature of property decorator when called as an expression. Supplied parameters do not match any signature of call target. @@ -8,6 +8,6 @@ tests/cases/conformance/decorators/class/property/decoratorOnClassProperty11.ts( class C { @dec prop; ~~~~ -!!! error TS1220: Invalid expression for property decorator. +!!! error TS1220: Unable to resolve signature of property decorator when called as an expression. !!! error TS1220: Supplied parameters do not match any signature of call target. } \ No newline at end of file diff --git a/tests/baselines/reference/decoratorOnClassProperty6.errors.txt b/tests/baselines/reference/decoratorOnClassProperty6.errors.txt index 441ff38a421..eca97e803e0 100644 --- a/tests/baselines/reference/decoratorOnClassProperty6.errors.txt +++ b/tests/baselines/reference/decoratorOnClassProperty6.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/decorators/class/property/decoratorOnClassProperty6.ts(4,5): error TS1220: Invalid expression for property decorator. +tests/cases/conformance/decorators/class/property/decoratorOnClassProperty6.ts(4,5): error TS1220: Unable to resolve signature of property decorator when called as an expression. Supplied parameters do not match any signature of call target. @@ -8,6 +8,6 @@ tests/cases/conformance/decorators/class/property/decoratorOnClassProperty6.ts(4 class C { @dec prop; ~~~~ -!!! error TS1220: Invalid expression for property decorator. +!!! error TS1220: Unable to resolve signature of property decorator when called as an expression. !!! error TS1220: Supplied parameters do not match any signature of call target. } \ No newline at end of file diff --git a/tests/baselines/reference/decoratorOnClassProperty7.errors.txt b/tests/baselines/reference/decoratorOnClassProperty7.errors.txt index e061ce446ac..7f2249e5934 100644 --- a/tests/baselines/reference/decoratorOnClassProperty7.errors.txt +++ b/tests/baselines/reference/decoratorOnClassProperty7.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/decorators/class/property/decoratorOnClassProperty7.ts(4,5): error TS1220: Invalid expression for property decorator. +tests/cases/conformance/decorators/class/property/decoratorOnClassProperty7.ts(4,5): error TS1220: Unable to resolve signature of property decorator when called as an expression. Supplied parameters do not match any signature of call target. @@ -8,6 +8,6 @@ tests/cases/conformance/decorators/class/property/decoratorOnClassProperty7.ts(4 class C { @dec prop; ~~~~ -!!! error TS1220: Invalid expression for property decorator. +!!! error TS1220: Unable to resolve signature of property decorator when called as an expression. !!! error TS1220: Supplied parameters do not match any signature of call target. } \ No newline at end of file