Merge pull request #3249 from Microsoft/resolveDecoratorAsCall

Migrated decorator checks to call resolution
This commit is contained in:
Ron Buckton
2015-06-24 09:58:24 -07:00
51 changed files with 703 additions and 316 deletions
+482 -65
View File
@@ -118,10 +118,7 @@ namespace ts {
let globalIterableIteratorType: GenericType;
let anyArrayType: Type;
let getGlobalClassDecoratorType: () => ObjectType;
let getGlobalParameterDecoratorType: () => ObjectType;
let getGlobalPropertyDecoratorType: () => ObjectType;
let getGlobalMethodDecoratorType: () => ObjectType;
let getGlobalTypedPropertyDescriptorType: () => ObjectType;
let tupleTypes: Map<TupleType> = {};
let unionTypes: Map<UnionType> = {};
@@ -3793,6 +3790,16 @@ namespace ts {
return globalESSymbolConstructorSymbol || (globalESSymbolConstructorSymbol = getGlobalValueSymbol("Symbol"));
}
/**
* Creates a TypeReference for a generic `TypedPropertyDescriptor<T>`.
*/
function createTypedPropertyDescriptorType(propertyType: Type): Type {
let globalTypedPropertyDescriptorType = getGlobalTypedPropertyDescriptorType();
return globalTypedPropertyDescriptorType !== emptyObjectType
? createTypeReference(<GenericType>globalTypedPropertyDescriptorType, [propertyType])
: emptyObjectType;
}
/**
* Instantiates a global type that is generic with some element type, and returns that instantiation.
*/
@@ -4276,8 +4283,8 @@ namespace ts {
return checkTypeRelatedTo(source, target, subtypeRelation, errorNode, headMessage, containingMessageChain);
}
function checkTypeAssignableTo(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage): boolean {
return checkTypeRelatedTo(source, target, assignableRelation, errorNode, headMessage);
function checkTypeAssignableTo(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: DiagnosticMessageChain): boolean {
return checkTypeRelatedTo(source, target, assignableRelation, errorNode, headMessage, containingMessageChain);
}
function isSignatureAssignableTo(source: Signature, target: Signature): boolean {
@@ -6938,7 +6945,7 @@ namespace ts {
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
checkExpression((<TaggedTemplateExpression>node).template);
}
else {
else if (node.kind !== SyntaxKind.Decorator) {
forEach((<CallExpression>node).arguments, argument => {
checkExpression(argument);
});
@@ -7007,7 +7014,8 @@ namespace ts {
function getSpreadArgumentIndex(args: Expression[]): number {
for (let i = 0; i < args.length; i++) {
if (args[i].kind === SyntaxKind.SpreadElementExpression) {
let arg = args[i];
if (arg && arg.kind === SyntaxKind.SpreadElementExpression) {
return i;
}
}
@@ -7018,7 +7026,9 @@ namespace ts {
let adjustedArgCount: number; // Apparent number of arguments we will have in this call
let typeArguments: NodeArray<TypeNode>; // Type arguments (undefined if none)
let callIsIncomplete: boolean; // In incomplete call we want to be lenient when we have too few arguments
let isDecorator: boolean;
let spreadArgIndex = -1;
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
let tagExpression = <TaggedTemplateExpression>node;
@@ -7044,6 +7054,11 @@ namespace ts {
callIsIncomplete = !!templateLiteral.isUnterminated;
}
}
else if (node.kind === SyntaxKind.Decorator) {
isDecorator = true;
typeArguments = undefined;
adjustedArgCount = getEffectiveArgumentCount(node, /*args*/ undefined, signature);
}
else {
let callExpression = <CallExpression>node;
if (!callExpression.arguments) {
@@ -7060,6 +7075,7 @@ namespace ts {
callIsIncomplete = (<CallExpression>callExpression).arguments.end === callExpression.end;
typeArguments = callExpression.typeArguments;
spreadArgIndex = getSpreadArgumentIndex(args);
}
// If the user supplied type arguments, but the number of type arguments does not match
@@ -7072,7 +7088,6 @@ namespace ts {
// If spread arguments are present, check that they correspond to a rest parameter. If so, no
// further checking is necessary.
let spreadArgIndex = getSpreadArgumentIndex(args);
if (spreadArgIndex >= 0) {
return signature.hasRestParameter && spreadArgIndex >= signature.parameters.length - 1;
}
@@ -7109,7 +7124,7 @@ namespace ts {
return getSignatureInstantiation(signature, getInferredTypes(context));
}
function inferTypeArguments(signature: Signature, args: Expression[], excludeArgument: boolean[], context: InferenceContext): void {
function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: Expression[], excludeArgument: boolean[], context: InferenceContext): void {
let typeParameters = signature.typeParameters;
let inferenceMapper = createInferenceMapper(context);
@@ -7137,20 +7152,23 @@ namespace ts {
// We perform two passes over the arguments. In the first pass we infer from all arguments, but use
// wildcards for all context sensitive function expressions.
for (let i = 0; i < args.length; i++) {
let arg = args[i];
if (arg.kind !== SyntaxKind.OmittedExpression) {
let argCount = getEffectiveArgumentCount(node, args, signature);
for (let i = 0; i < argCount; i++) {
let arg = getEffectiveArgument(node, args, i);
// If the effective argument is 'undefined', then it is an argument that is present but is synthetic.
if (arg === undefined || arg.kind !== SyntaxKind.OmittedExpression) {
let paramType = getTypeAtPosition(signature, i);
let argType: Type;
if (i === 0 && args[i].parent.kind === SyntaxKind.TaggedTemplateExpression) {
argType = globalTemplateStringsArrayType;
}
else {
let argType = getEffectiveArgumentType(node, i, arg);
// If the effective argument type is 'undefined', there is no synthetic type
// for the argument. In that case, we should check the argument.
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);
}
}
@@ -7158,8 +7176,10 @@ namespace ts {
// In the second pass we visit only context sensitive arguments, and only those that aren't excluded, this
// time treating function expressions normally (which may cause previously inferred type arguments to be fixed
// as we construct types for contextually typed parameters)
// Decorators will not have `excludeArgument`, as their arguments cannot be contextually typed.
// Tagged template expressions will always have `undefined` for `excludeArgument[0]`.
if (excludeArgument) {
for (let i = 0; i < args.length; i++) {
for (let i = 0; i < argCount; i++) {
// No need to check for omitted args and template expressions, their exlusion value is always undefined
if (excludeArgument[i] === false) {
let arg = args[i];
@@ -7172,7 +7192,7 @@ namespace ts {
getInferredTypes(context);
}
function checkTypeArguments(signature: Signature, typeArguments: TypeNode[], typeArgumentResultTypes: Type[], reportErrors: boolean): boolean {
function checkTypeArguments(signature: Signature, typeArguments: TypeNode[], typeArgumentResultTypes: Type[], reportErrors: boolean, headMessage?: DiagnosticMessage): boolean {
let typeParameters = signature.typeParameters;
let typeArgumentsAreAssignable = true;
for (let i = 0; i < typeParameters.length; i++) {
@@ -7183,35 +7203,53 @@ namespace ts {
if (typeArgumentsAreAssignable /* so far */) {
let constraint = getConstraintOfTypeParameter(typeParameters[i]);
if (constraint) {
typeArgumentsAreAssignable = checkTypeAssignableTo(typeArgument, constraint, reportErrors ? typeArgNode : undefined,
Diagnostics.Type_0_does_not_satisfy_the_constraint_1);
let errorInfo: DiagnosticMessageChain;
let typeArgumentHeadMessage = Diagnostics.Type_0_does_not_satisfy_the_constraint_1;
if (reportErrors && headMessage) {
errorInfo = chainDiagnosticMessages(errorInfo, typeArgumentHeadMessage);
typeArgumentHeadMessage = headMessage;
}
typeArgumentsAreAssignable = checkTypeAssignableTo(
typeArgument,
constraint,
reportErrors ? typeArgNode : undefined,
typeArgumentHeadMessage,
errorInfo);
}
}
}
return typeArgumentsAreAssignable;
}
function checkApplicableSignature(node: CallLikeExpression, args: Expression[], signature: Signature, relation: Map<RelationComparisonResult>, excludeArgument: boolean[], reportErrors: boolean) {
for (let i = 0; i < args.length; i++) {
let arg = args[i];
if (arg.kind !== SyntaxKind.OmittedExpression) {
let argCount = getEffectiveArgumentCount(node, args, signature);
for (let i = 0; i < argCount; i++) {
let arg = getEffectiveArgument(node, args, i);
// If the effective argument is 'undefined', then it is an argument that is present but is synthetic.
if (arg === undefined || 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
// unless we're reporting errors
let argType = i === 0 && node.kind === SyntaxKind.TaggedTemplateExpression
? globalTemplateStringsArrayType
: arg.kind === SyntaxKind.StringLiteral && !reportErrors
let argType = getEffectiveArgumentType(node, i, arg);
// If the effective argument type is 'undefined', there is no synthetic type
// for the argument. In that case, we should check the argument.
if (argType === undefined) {
argType = arg.kind === SyntaxKind.StringLiteral && !reportErrors
? getStringLiteralType(<StringLiteral>arg)
: 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)) {
let errorNode = reportErrors ? getEffectiveArgumentErrorNode(node, i, arg) : undefined;
let headMessage = Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1;
if (!checkTypeRelatedTo(argType, paramType, relation, errorNode, headMessage)) {
return false;
}
}
}
return true;
}
@@ -7220,20 +7258,27 @@ namespace ts {
*
* If 'node' is a CallExpression or a NewExpression, then its argument list is returned.
* 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.
* expressions, where the first element of the list is `undefined`.
* If 'node' is a Decorator, the argument list will be `undefined`, and its arguments and types
* will be supplied from calls to `getEffectiveArgumentCount` and `getEffectiveArgumentType`.
*/
function getEffectiveCallArguments(node: CallLikeExpression): Expression[] {
let args: Expression[];
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
let template = (<TaggedTemplateExpression>node).template;
args = [template];
args = [undefined];
if (template.kind === SyntaxKind.TemplateExpression) {
forEach((<TemplateExpression>template).templateSpans, span => {
args.push(span.expression);
});
}
}
else if (node.kind === SyntaxKind.Decorator) {
// For a decorator, we return undefined as we will determine
// the number and types of arguments for a decorator using
// `getEffectiveArgumentCount` and `getEffectiveArgumentType` below.
return undefined;
}
else {
args = (<CallExpression>node).arguments || emptyArray;
}
@@ -7241,12 +7286,284 @@ namespace ts {
return args;
}
function resolveCall(node: CallLikeExpression, signatures: Signature[], candidatesOutArray: Signature[]): Signature {
/**
* Returns the effective argument count for a node that works like a function invocation.
* If 'node' is a Decorator, the number of arguments is derived from the decoration
* target and the signature:
* If 'node.target' is a class declaration or class expression, the effective argument
* count is 1.
* If 'node.target' is a parameter declaration, the effective argument count is 3.
* If 'node.target' is a property declaration, the effective argument count is 2.
* If 'node.target' is a method or accessor declaration, the effective argument count
* is 3, although it can be 2 if the signature only accepts two arguments, allowing
* us to match a property decorator.
* Otherwise, the argument count is the length of the 'args' array.
*/
function getEffectiveArgumentCount(node: CallLikeExpression, args: Expression[], signature: Signature) {
if (node.kind === SyntaxKind.Decorator) {
switch (node.parent.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
// A class decorator will have one argument (see `ClassDecorator` in core.d.ts)
return 1;
case SyntaxKind.PropertyDeclaration:
// A property declaration decorator will have two arguments (see
// `PropertyDecorator` in core.d.ts)
return 2;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
// A method or accessor declaration decorator will have two or three arguments (see
// `PropertyDecorator` and `MethodDecorator` in core.d.ts)
// If the method decorator signature only accepts a target and a key, we will only
// type check those arguments.
return signature.parameters.length >= 3 ? 3 : 2;
case SyntaxKind.Parameter:
// A parameter declaration decorator will have three arguments (see
// `ParameterDecorator` in core.d.ts)
return 3;
}
}
else {
return args.length;
}
}
/**
* Returns the effective type of the first argument to a decorator.
* If 'node' is a class declaration or class expression, the effective argument type
* is the type of the static side of the class.
* If 'node' is a parameter declaration, the effective argument type is either the type
* of the static or instance side of the class for the parameter's parent method,
* depending on whether the method is declared static.
* For a constructor, the type is always the type of the static side of the class.
* If 'node' is a property, method, or accessor declaration, the effective argument
* type is the type of the static or instance side of the parent class for class
* element, depending on whether the element is declared static.
*/
function getEffectiveDecoratorFirstArgumentType(node: Node): Type {
// The first argument to a decorator is its `target`.
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
// 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(node);
return getTypeOfSymbol(classSymbol);
case SyntaxKind.Parameter:
// For a parameter decorator, the `target` is the parent type of the
// parameter's containing method.
node = node.parent;
if (node.kind === SyntaxKind.Constructor) {
let classSymbol = getSymbolOfNode(node);
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 getParentTypeOfClassElement(<ClassElement>node);
default:
Debug.fail("Unsupported decorator target.");
return unknownType;
}
}
/**
* Returns the effective type for the second argument to a decorator.
* If 'node' is a parameter, its effective argument type is one of the following:
* If 'node.parent' is a constructor, the effective argument type is 'any', as we
* will emit `undefined`.
* If 'node.parent' is a member with an identifier, numeric, or string literal name,
* the effective argument type will be a string literal type for the member name.
* If 'node.parent' is a computed property name, the effective argument type will
* either be a symbol type or the string type.
* If 'node' is a member with an identifier, numeric, or string literal name, the
* effective argument type will be a string literal type for the member name.
* If 'node' is a computed property name, the effective argument type will either
* be a symbol type or the string type.
* A class decorator does not have a second argument type.
*/
function getEffectiveDecoratorSecondArgumentType(node: Node) {
// The second argument to a decorator is its `propertyKey`
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
Debug.fail("Class decorators should not have a second synthetic argument.");
return unknownType;
case SyntaxKind.Parameter:
node = node.parent;
if (node.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 = <ClassElement>node;
switch (element.name.kind) {
case SyntaxKind.Identifier:
case SyntaxKind.NumericLiteral:
case SyntaxKind.StringLiteral:
return getStringLiteralType(<StringLiteral>element.name);
case SyntaxKind.ComputedPropertyName:
let nameType = checkComputedPropertyName(<ComputedPropertyName>element.name);
if (allConstituentTypesHaveKind(nameType, TypeFlags.ESSymbol)) {
return nameType;
}
else {
return stringType;
}
default:
Debug.fail("Unsupported property name.");
return unknownType;
}
default:
Debug.fail("Unsupported decorator target.");
return unknownType;
}
}
/**
* Returns the effective argument type for the third argument to a decorator.
* If 'node' is a parameter, the effective argument type is the number type.
* If 'node' is a method or accessor, the effective argument type is a
* `TypedPropertyDescriptor<T>` instantiated with the type of the member.
* Class and property decorators do not have a third effective argument.
*/
function getEffectiveDecoratorThirdArgumentType(node: Node) {
// The third argument to a decorator is either its `descriptor` for a method decorator
// or its `parameterIndex` for a paramter decorator
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
Debug.fail("Class decorators should not have a third synthetic argument.");
return unknownType;
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.");
return unknownType;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
// The `descriptor` for a method decorator will be a `TypedPropertyDescriptor<T>`
// for the type of the member.
let propertyType = getTypeOfNode(node);
return createTypedPropertyDescriptorType(propertyType);
default:
Debug.fail("Unsupported decorator target.");
return unknownType;
}
}
/**
* Returns the effective argument type for the provided argument to a decorator.
*/
function getEffectiveDecoratorArgumentType(node: Decorator, argIndex: number): Type {
if (argIndex === 0) {
return getEffectiveDecoratorFirstArgumentType(node.parent);
}
else if (argIndex === 1) {
return getEffectiveDecoratorSecondArgumentType(node.parent);
}
else if (argIndex === 2) {
return getEffectiveDecoratorThirdArgumentType(node.parent);
}
Debug.fail("Decorators should not have a fourth synthetic argument.");
return unknownType;
}
/**
* Gets the effective argument type for an argument in a call expression.
*/
function getEffectiveArgumentType(node: CallLikeExpression, argIndex: number, arg: Expression): Type {
// 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
if (node.kind === SyntaxKind.Decorator) {
return getEffectiveDecoratorArgumentType(<Decorator>node, argIndex);
}
else if (argIndex === 0 && node.kind === SyntaxKind.TaggedTemplateExpression) {
return globalTemplateStringsArrayType;
}
// This is not a synthetic argument, so we return 'undefined'
// to signal that the caller needs to check the argument.
return undefined;
}
/**
* Gets the effective argument expression for an argument in a call expression.
*/
function getEffectiveArgument(node: CallLikeExpression, args: Expression[], argIndex: number) {
// For a decorator or the first argument of a tagged template expression we return undefined.
if (node.kind === SyntaxKind.Decorator ||
(argIndex === 0 && node.kind === SyntaxKind.TaggedTemplateExpression)) {
return undefined;
}
return args[argIndex];
}
/**
* Gets the error node to use when reporting errors for an effective argument.
*/
function getEffectiveArgumentErrorNode(node: CallLikeExpression, argIndex: number, arg: Expression) {
if (node.kind === SyntaxKind.Decorator) {
// For a decorator, we use the expression of the decorator for error reporting.
return (<Decorator>node).expression;
}
else if (argIndex === 0 && node.kind === SyntaxKind.TaggedTemplateExpression) {
// For a the first argument of a tagged template expression, we use the template of the tag for error reporting.
return (<TaggedTemplateExpression>node).template;
}
else {
return arg;
}
}
function resolveCall(node: CallLikeExpression, signatures: Signature[], candidatesOutArray: Signature[], headMessage?: DiagnosticMessage): Signature {
let isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression;
let isDecorator = node.kind === SyntaxKind.Decorator;
let typeArguments: TypeNode[];
if (!isTaggedTemplate) {
if (!isTaggedTemplate && !isDecorator) {
typeArguments = (<CallExpression>node).typeArguments;
// We already perform checking on the type arguments on the class declaration itself.
@@ -7259,7 +7576,7 @@ namespace ts {
// reorderCandidates fills up the candidates array directly
reorderCandidates(signatures, candidates);
if (!candidates.length) {
error(node, Diagnostics.Supplied_parameters_do_not_match_any_signature_of_call_target);
reportError(Diagnostics.Supplied_parameters_do_not_match_any_signature_of_call_target);
return resolveErrorCall(node);
}
@@ -7276,13 +7593,20 @@ namespace 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[];
for (let i = isTaggedTemplate ? 1 : 0; i < args.length; i++) {
if (isContextSensitive(args[i])) {
if (!excludeArgument) {
excludeArgument = new Array(args.length);
if (!isDecorator) {
// We do not need to call `getEffectiveArgumentCount` here as it only
// applies when calculating the number of arguments for a decorator.
for (let i = isTaggedTemplate ? 1 : 0; i < args.length; i++) {
if (isContextSensitive(args[i])) {
if (!excludeArgument) {
excludeArgument = new Array(args.length);
}
excludeArgument[i] = true;
}
excludeArgument[i] = true;
}
}
@@ -7349,8 +7673,8 @@ namespace ts {
checkApplicableSignature(node, args, candidateForArgumentError, assignableRelation, /*excludeArgument*/ undefined, /*reportErrors*/ true);
}
else if (candidateForTypeArgumentError) {
if (!isTaggedTemplate && typeArguments) {
checkTypeArguments(candidateForTypeArgumentError, typeArguments, [], /*reportErrors*/ true)
if (!isTaggedTemplate && !isDecorator && typeArguments) {
checkTypeArguments(candidateForTypeArgumentError, (<CallExpression>node).typeArguments, [], /*reportErrors*/ true, headMessage)
}
else {
Debug.assert(resultOfFailedInference.failedTypeParameterIndex >= 0);
@@ -7360,12 +7684,16 @@ namespace ts {
let diagnosticChainHead = chainDiagnosticMessages(/*details*/ undefined, // details will be provided by call to reportNoCommonSupertypeError
Diagnostics.The_type_argument_for_type_parameter_0_cannot_be_inferred_from_the_usage_Consider_specifying_the_type_arguments_explicitly,
typeToString(failedTypeParameter));
if (headMessage) {
diagnosticChainHead = chainDiagnosticMessages(diagnosticChainHead, headMessage);
}
reportNoCommonSupertypeError(inferenceCandidates, (<CallExpression>node).expression || (<TaggedTemplateExpression>node).tag, diagnosticChainHead);
}
}
else {
error(node, Diagnostics.Supplied_parameters_do_not_match_any_signature_of_call_target);
reportError(Diagnostics.Supplied_parameters_do_not_match_any_signature_of_call_target);
}
// No signature was applicable. We have already reported the errors for the invalid signature.
@@ -7382,6 +7710,16 @@ namespace ts {
}
return resolveErrorCall(node);
function reportError(message: DiagnosticMessage, arg0?: string, arg1?: string, arg2?: string): void {
let errorInfo: DiagnosticMessageChain;
errorInfo = chainDiagnosticMessages(errorInfo, message, arg0, arg1, arg2);
if (headMessage) {
errorInfo = chainDiagnosticMessages(errorInfo, headMessage);
}
diagnostics.add(createDiagnosticForNodeFromMessageChain(node, errorInfo));
}
function chooseOverload(candidates: Signature[], relation: Map<RelationComparisonResult>) {
for (let originalCandidate of candidates) {
@@ -7404,7 +7742,7 @@ namespace ts {
typeArgumentsAreValid = checkTypeArguments(candidate, typeArguments, typeArgumentTypes, /*reportErrors*/ false)
}
else {
inferTypeArguments(candidate, args, excludeArgument, inferenceContext);
inferTypeArguments(node, candidate, args, excludeArgument, inferenceContext);
typeArgumentsAreValid = inferenceContext.failedTypeParameterIndex === undefined;
typeArgumentTypes = inferenceContext.inferredTypes;
}
@@ -7588,6 +7926,55 @@ namespace ts {
return resolveCall(node, callSignatures, candidatesOutArray);
}
/**
* Gets the localized diagnostic head message to use for errors when resolving a decorator as a call expression.
*/
function getDiagnosticHeadMessageForDecoratorResolution(node: Decorator) {
switch (node.parent.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
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;
case SyntaxKind.PropertyDeclaration:
return Diagnostics.Unable_to_resolve_signature_of_property_decorator_when_called_as_an_expression;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return Diagnostics.Unable_to_resolve_signature_of_method_decorator_when_called_as_an_expression;
}
}
/**
* Resolves a decorator as if it were a call expression.
*/
function resolveDecorator(node: Decorator, candidatesOutArray: Signature[]): Signature {
let funcType = checkExpression(node.expression);
let apparentType = getApparentType(funcType);
if (apparentType === unknownType) {
return resolveErrorCall(node);
}
let callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call);
if (funcType === anyType || (!callSignatures.length && !(funcType.flags & TypeFlags.Union) && isTypeAssignableTo(funcType, globalFunctionType))) {
return resolveUntypedCall(node);
}
let headMessage = getDiagnosticHeadMessageForDecoratorResolution(node);
if (!callSignatures.length) {
let errorInfo: DiagnosticMessageChain;
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature);
errorInfo = chainDiagnosticMessages(errorInfo, headMessage);
diagnostics.add(createDiagnosticForNodeFromMessageChain(node, errorInfo));
return resolveErrorCall(node);
}
return resolveCall(node, callSignatures, candidatesOutArray, headMessage);
}
// candidatesOutArray is passed by signature help in the language service, and collectCandidates
// must fill it up with the appropriate candidate signatures
@@ -7609,6 +7996,9 @@ namespace ts {
else if (node.kind === SyntaxKind.TaggedTemplateExpression) {
links.resolvedSignature = resolveTaggedTemplateExpression(<TaggedTemplateExpression>node, candidatesOutArray);
}
else if (node.kind === SyntaxKind.Decorator) {
links.resolvedSignature = resolveDecorator(<Decorator>node, candidatesOutArray);
}
else {
Debug.fail("Branch in 'getResolvedSignature' should be unreachable.");
}
@@ -9404,35 +9794,54 @@ namespace ts {
/** Check a decorator */
function checkDecorator(node: Decorator): void {
let expression: Expression = node.expression;
let exprType = checkExpression(expression);
let signature = getResolvedSignature(node);
let returnType = getReturnTypeOfSignature(signature);
if (returnType.flags & TypeFlags.Any) {
return;
}
let expectedReturnType: Type;
let headMessage = getDiagnosticHeadMessageForDecoratorResolution(node);
let errorInfo: DiagnosticMessageChain;
switch (node.parent.kind) {
case SyntaxKind.ClassDeclaration:
let classSymbol = getSymbolOfNode(node.parent);
let classConstructorType = getTypeOfSymbol(classSymbol);
let classDecoratorType = instantiateSingleCallFunctionType(getGlobalClassDecoratorType(), [classConstructorType]);
checkTypeAssignableTo(exprType, classDecoratorType, node);
expectedReturnType = getUnionType([classConstructorType, voidType]);
break;
case SyntaxKind.Parameter:
expectedReturnType = voidType;
errorInfo = chainDiagnosticMessages(
errorInfo,
Diagnostics.The_return_type_of_a_parameter_decorator_function_must_be_either_void_or_any);
break;
case SyntaxKind.PropertyDeclaration:
checkTypeAssignableTo(exprType, getGlobalPropertyDecoratorType(), node);
expectedReturnType = voidType;
errorInfo = chainDiagnosticMessages(
errorInfo,
Diagnostics.The_return_type_of_a_property_decorator_function_must_be_either_void_or_any);
break;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
let methodType = getTypeOfNode(node.parent);
let methodDecoratorType = instantiateSingleCallFunctionType(getGlobalMethodDecoratorType(), [methodType]);
checkTypeAssignableTo(exprType, methodDecoratorType, node);
break;
case SyntaxKind.Parameter:
checkTypeAssignableTo(exprType, getGlobalParameterDecoratorType(), node);
let descriptorType = createTypedPropertyDescriptorType(methodType);
expectedReturnType = getUnionType([descriptorType, voidType]);
break;
}
checkTypeAssignableTo(
returnType,
expectedReturnType,
node,
headMessage,
errorInfo);
}
/** Checks a type reference node as an expression. */
function checkTypeNodeAsExpression(node: TypeNode) {
// When we are emitting type metadata for decorators, we need to try to check the type
@@ -12058,6 +12467,17 @@ namespace ts {
return checkExpression(expr);
}
/**
* Gets either the static or instance type of a class element, based on
* whether the element is declared as "static".
*/
function getParentTypeOfClassElement(node: ClassElement) {
let classSymbol = getSymbolOfNode(node.parent);
return node.flags & NodeFlags.Static
? getTypeOfSymbol(classSymbol)
: getDeclaredTypeOfSymbol(classSymbol);
}
// Return the list of properties of the given type, augmented with properties from Function
// if the type has call or construct signatures
function getAugmentedPropertiesOfType(type: Type): Symbol[] {
@@ -12594,10 +13014,7 @@ namespace ts {
globalNumberType = getGlobalType("Number");
globalBooleanType = getGlobalType("Boolean");
globalRegExpType = getGlobalType("RegExp");
getGlobalClassDecoratorType = memoize(() => getGlobalType("ClassDecorator"));
getGlobalPropertyDecoratorType = memoize(() => getGlobalType("PropertyDecorator"));
getGlobalMethodDecoratorType = memoize(() => getGlobalType("MethodDecorator"));
getGlobalParameterDecoratorType = memoize(() => getGlobalType("ParameterDecorator"));
getGlobalTypedPropertyDescriptorType = memoize(() => getGlobalType("TypedPropertyDescriptor", /*arity*/ 1));
// If we're in ES6 mode, load the TemplateStringsArray.
// Otherwise, default to 'unknown' for the purposes of type checking in LS scenarios.
@@ -191,6 +191,12 @@ namespace ts {
An_export_declaration_can_only_be_used_in_a_module: { code: 1233, category: DiagnosticCategory.Error, key: "An export declaration can only be used in a module." },
An_ambient_module_declaration_is_only_allowed_at_the_top_level_in_a_file: { code: 1234, category: DiagnosticCategory.Error, key: "An ambient module declaration is only allowed at the top level in a file." },
A_namespace_declaration_is_only_allowed_in_a_namespace_or_module: { code: 1235, category: DiagnosticCategory.Error, key: "A namespace declaration is only allowed in a namespace or module." },
The_return_type_of_a_property_decorator_function_must_be_either_void_or_any: { code: 1236, category: DiagnosticCategory.Error, key: "The return type of a property decorator function must be either 'void' or 'any'." },
The_return_type_of_a_parameter_decorator_function_must_be_either_void_or_any: { code: 1237, category: DiagnosticCategory.Error, key: "The return type of a parameter decorator function must be either 'void' or 'any'." },
Unable_to_resolve_signature_of_class_decorator_when_called_as_an_expression: { code: 1238, category: DiagnosticCategory.Error, key: "Unable to resolve signature of class decorator when called as an expression." },
Unable_to_resolve_signature_of_parameter_decorator_when_called_as_an_expression: { code: 1239, category: DiagnosticCategory.Error, key: "Unable to resolve signature of parameter decorator when called as an expression." },
Unable_to_resolve_signature_of_property_decorator_when_called_as_an_expression: { code: 1240, category: DiagnosticCategory.Error, key: "Unable to resolve signature of property decorator when called as an expression." },
Unable_to_resolve_signature_of_method_decorator_when_called_as_an_expression: { code: 1241, category: DiagnosticCategory.Error, key: "Unable to resolve signature of method 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." },
+25
View File
@@ -753,6 +753,31 @@
},
"The return type of a property decorator function must be either 'void' or 'any'.": {
"category": "Error",
"code": 1236
},
"The return type of a parameter decorator function must be either 'void' or 'any'.": {
"category": "Error",
"code": 1237
},
"Unable to resolve signature of class decorator when called as an expression.": {
"category": "Error",
"code": 1238
},
"Unable to resolve signature of parameter decorator when called as an expression.": {
"category": "Error",
"code": 1239
},
"Unable to resolve signature of property decorator when called as an expression.": {
"category": "Error",
"code": 1240
},
"Unable to resolve signature of method decorator when called as an expression.": {
"category": "Error",
"code": 1241
},
"Duplicate identifier '{0}'.": {
"category": "Error",
"code": 2300
+1 -1
View File
@@ -797,7 +797,7 @@ namespace ts {
template: LiteralExpression | TemplateExpression;
}
export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression;
export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator;
export interface TypeAssertion extends UnaryExpression {
type: TypeNode;
+2 -2
View File
@@ -752,8 +752,8 @@ namespace ts {
return (<TaggedTemplateExpression>node).tag;
}
// Will either be a CallExpression or NewExpression.
return (<CallExpression>node).expression;
// Will either be a CallExpression, NewExpression, or Decorator.
return (<CallExpression | Decorator>node).expression;
}
export function nodeCanBeDecorated(node: Node): boolean {