diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 935f5ae334..bf42e584f0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2639,7 +2639,8 @@ namespace ts { } let baseConstructorType = checkExpression(baseTypeNode.expression); if (baseConstructorType.flags & TypeFlags.ObjectType) { - // Force resolution of members such that we catch circularities + // Resolving the members of a class requires us to resolve the base class of that class. + // We force resolution here such that we catch circularities now. resolveObjectOrUnionTypeMembers(baseConstructorType); } if (!popTypeResolution()) { @@ -2647,7 +2648,7 @@ namespace ts { return type.resolvedBaseConstructorType = unknownType; } if (baseConstructorType !== unknownType && baseConstructorType !== nullType && !isConstructorType(baseConstructorType)) { - error(baseTypeNode.expression, Diagnostics.Base_expression_is_not_of_a_constructor_function_type); + error(baseTypeNode.expression, Diagnostics.Type_0_is_not_a_constructor_function_type, typeToString(baseConstructorType)); return type.resolvedBaseConstructorType = unknownType; } type.resolvedBaseConstructorType = baseConstructorType; @@ -2699,7 +2700,7 @@ namespace ts { return; } if (!(getTargetType(baseType).flags & (TypeFlags.Class | TypeFlags.Interface))) { - error(baseTypeNode.expression, Diagnostics.Base_constructor_does_not_return_a_class_or_interface_type); + error(baseTypeNode.expression, Diagnostics.Base_constructor_return_type_0_is_not_a_class_or_interface_type, typeToString(baseType)); return; } if (type === baseType || hasBaseType(baseType, type)) { @@ -5962,7 +5963,9 @@ namespace ts { let baseClassType = classType && getBaseTypes(classType)[0]; if (!baseClassType) { - error(node, Diagnostics.super_can_only_be_referenced_in_a_derived_class); + if (!classDeclaration || !getClassExtendsHeritageClauseElement(classDeclaration)) { + error(node, Diagnostics.super_can_only_be_referenced_in_a_derived_class); + } return unknownType; } @@ -8970,13 +8973,14 @@ namespace ts { checkDecorators(node); } - function checkTypeArgumentsAndConstraints(typeParameters: TypeParameter[], typeArguments: TypeNode[]) { - for (let i = 0; i < typeArguments.length; i++) { - let typeArgument = typeArguments[i]; - checkSourceElement(typeArgument); - let constraint = getConstraintOfTypeParameter(typeParameters[i]); - if (produceDiagnostics && constraint) { - checkTypeAssignableTo(getTypeFromTypeNode(typeArgument), constraint, typeArgument, Diagnostics.Type_0_does_not_satisfy_the_constraint_1); + function checkTypeArgumentConstraints(typeParameters: TypeParameter[], typeArguments: TypeNode[]) { + if (produceDiagnostics) { + for (let i = 0; i < typeParameters.length; i++) { + let constraint = getConstraintOfTypeParameter(typeParameters[i]); + if (constraint) { + let typeArgument = typeArguments[i]; + checkTypeAssignableTo(getTypeFromTypeNode(typeArgument), constraint, typeArgument, Diagnostics.Type_0_does_not_satisfy_the_constraint_1); + } } } } @@ -8986,9 +8990,10 @@ namespace ts { let type = getTypeFromTypeReference(node); if (type !== unknownType && node.typeArguments) { // Do type argument local checks only if referenced type is successfully resolved + forEach(node.typeArguments, checkSourceElement); let symbol = getNodeLinks(node).resolvedSymbol; let typeParameters = symbol.flags & SymbolFlags.TypeAlias ? getSymbolLinks(symbol).typeParameters : (type).target.localTypeParameters; - checkTypeArgumentsAndConstraints(typeParameters, node.typeArguments); + checkTypeArgumentConstraints(typeParameters, node.typeArguments); } } @@ -10588,15 +10593,19 @@ namespace ts { let baseType = baseTypes[0]; let staticBaseType = getBaseConstructorTypeOfClass(type); if (baseTypeNode.typeArguments) { - let constructors = getConstructorsForTypeArguments(staticBaseType, baseTypeNode.typeArguments); - checkTypeArgumentsAndConstraints(constructors[0].typeParameters, baseTypeNode.typeArguments); + forEach(baseTypeNode.typeArguments, checkSourceElement); + for (let constructor of getConstructorsForTypeArguments(staticBaseType, baseTypeNode.typeArguments)) { + checkTypeArgumentConstraints(constructor.typeParameters, baseTypeNode.typeArguments); + } } checkTypeAssignableTo(type, baseType, node.name || node, Diagnostics.Class_0_incorrectly_extends_base_class_1); checkTypeAssignableTo(staticType, getTypeWithoutSignatures(staticBaseType), node.name || node, Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1); if (!(staticBaseType.symbol && staticBaseType.symbol.flags & SymbolFlags.Class)) { // When the static base type is a "class-like" constructor function (but not actually a class), we verify - // that all instantiated base constructor signatures return the same type. + // that all instantiated base constructor signatures return the same type. We can simply compare the type + // references (as opposed to checking the structure of the types) because elsewhere we have already checked + // that the base type is a class or interface type (and not, for example, an anonymous object type). let constructors = getInstantiatedConstructorsForTypeArguments(staticBaseType, baseTypeNode.typeArguments); if (forEach(constructors, sig => getReturnTypeOfSignature(sig) !== baseType)) { error(baseTypeNode.expression, Diagnostics.Base_constructors_must_all_have_the_same_return_type); @@ -11997,7 +12006,7 @@ namespace ts { return getTypeOfExpression(node); } - if (isClassExtendsExpressionWithTypeArguments(node)) { + if (isExpressionWithTypeArgumentsInClassExtendsClause(node)) { // A SyntaxKind.ExpressionWithTypeArguments is considered a type node, except when it occurs in the // extends clause of a class. We handle that case here. return getBaseTypes(getDeclaredTypeOfSymbol(getSymbolOfNode(node.parent.parent)))[0]; diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 2eada488a6..09b009711f 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -385,9 +385,9 @@ namespace ts { No_best_common_type_exists_among_yield_expressions: { code: 2504, category: DiagnosticCategory.Error, key: "No best common type exists among yield expressions." }, A_generator_cannot_have_a_void_type_annotation: { code: 2505, category: DiagnosticCategory.Error, key: "A generator cannot have a 'void' type annotation." }, _0_is_referenced_directly_or_indirectly_in_its_own_base_expression: { code: 2506, category: DiagnosticCategory.Error, key: "'{0}' is referenced directly or indirectly in its own base expression." }, - Base_expression_is_not_of_a_constructor_function_type: { code: 2507, category: DiagnosticCategory.Error, key: "Base expression is not of a constructor function type." }, + Type_0_is_not_a_constructor_function_type: { code: 2507, category: DiagnosticCategory.Error, key: "Type '{0}' is not a constructor function type." }, No_base_constructor_has_the_specified_number_of_type_arguments: { code: 2508, category: DiagnosticCategory.Error, key: "No base constructor has the specified number of type arguments." }, - Base_constructor_does_not_return_a_class_or_interface_type: { code: 2509, category: DiagnosticCategory.Error, key: "Base constructor does not return a class or interface type." }, + Base_constructor_return_type_0_is_not_a_class_or_interface_type: { code: 2509, category: DiagnosticCategory.Error, key: "Base constructor return type '{0}' is not a class or interface type." }, Base_constructors_must_all_have_the_same_return_type: { code: 2510, category: DiagnosticCategory.Error, key: "Base constructors must all have the same return type." }, Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." }, Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index dae42d20e2..fee46a84c1 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1529,7 +1529,7 @@ "category": "Error", "code": 2506 }, - "Base expression is not of a constructor function type.": { + "Type '{0}' is not a constructor function type.": { "category": "Error", "code": 2507 }, @@ -1537,7 +1537,7 @@ "category": "Error", "code": 2508 }, - "Base constructor does not return a class or interface type.": { + "Base constructor return type '{0}' is not a class or interface type.": { "category": "Error", "code": 2509 }, diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index dc55b9fac5..45b34f8b43 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -426,7 +426,7 @@ namespace ts { // Specialized signatures can have string literals as their parameters' type names return node.parent.kind === SyntaxKind.Parameter; case SyntaxKind.ExpressionWithTypeArguments: - return !isClassExtendsExpressionWithTypeArguments(node); + return !isExpressionWithTypeArgumentsInClassExtendsClause(node); // Identifiers and qualified names may be type nodes, depending on their context. Climb // above them to find the lowest container @@ -460,7 +460,7 @@ namespace ts { } switch (parent.kind) { case SyntaxKind.ExpressionWithTypeArguments: - return !isClassExtendsExpressionWithTypeArguments(parent); + return !isExpressionWithTypeArgumentsInClassExtendsClause(parent); case SyntaxKind.TypeParameter: return node === (parent).constraint; case SyntaxKind.PropertyDeclaration: @@ -920,7 +920,7 @@ namespace ts { case SyntaxKind.Decorator: return true; case SyntaxKind.ExpressionWithTypeArguments: - return isClassExtendsExpressionWithTypeArguments(parent); + return (parent).expression === node && isExpressionWithTypeArgumentsInClassExtendsClause(parent); default: if (isExpression(parent)) { return true; @@ -1914,7 +1914,7 @@ namespace ts { return token >= SyntaxKind.FirstAssignment && token <= SyntaxKind.LastAssignment; } - export function isClassExtendsExpressionWithTypeArguments(node: Node): boolean { + export function isExpressionWithTypeArgumentsInClassExtendsClause(node: Node): boolean { return node.kind === SyntaxKind.ExpressionWithTypeArguments && (node.parent).token === SyntaxKind.ExtendsKeyword && node.parent.parent.kind === SyntaxKind.ClassDeclaration; diff --git a/src/harness/typeWriter.ts b/src/harness/typeWriter.ts index ac695fc23a..b68e606c79 100644 --- a/src/harness/typeWriter.ts +++ b/src/harness/typeWriter.ts @@ -43,7 +43,7 @@ class TypeWriterWalker { // Workaround to ensure we output 'C' instead of 'typeof C' for base class expressions // var type = this.checker.getTypeAtLocation(node); - var type = node.parent && ts.isClassExtendsExpressionWithTypeArguments(node.parent) && this.checker.getTypeAtLocation(node.parent) || this.checker.getTypeAtLocation(node); + var type = node.parent && ts.isExpressionWithTypeArgumentsInClassExtendsClause(node.parent) && this.checker.getTypeAtLocation(node.parent) || this.checker.getTypeAtLocation(node); ts.Debug.assert(type !== undefined, "type doesn't exist"); var symbol = this.checker.getSymbolAtLocation(node); diff --git a/src/services/services.ts b/src/services/services.ts index 9d57ff7567..280386513d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -5812,7 +5812,7 @@ namespace ts { } return node.parent.kind === SyntaxKind.TypeReference || - (node.parent.kind === SyntaxKind.ExpressionWithTypeArguments && !isClassExtendsExpressionWithTypeArguments(node.parent)); + (node.parent.kind === SyntaxKind.ExpressionWithTypeArguments && !isExpressionWithTypeArgumentsInClassExtendsClause(node.parent)); } function isNamespaceReference(node: Node): boolean {