mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-15 12:51:30 -05:00
Improved type relation caching to fix #1002
This commit is contained in:
@@ -2163,7 +2163,7 @@ module ts {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < s.length; i++) {
|
||||
if (!compareSignatures(s[i], t[i], /*compareReturnTypes*/ false, isTypeIdenticalTo)) {
|
||||
if (!compareSignatures(s[i], t[i], /*compareReturnTypes*/ false, compareTypes)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -3209,38 +3209,32 @@ module ts {
|
||||
|
||||
// TYPE CHECKING
|
||||
|
||||
var subtypeRelation: Map<boolean> = {};
|
||||
var assignableRelation: Map<boolean> = {};
|
||||
var identityRelation: Map<boolean> = {};
|
||||
var subtypeRelation: Map<Ternary> = {};
|
||||
var assignableRelation: Map<Ternary> = {};
|
||||
var identityRelation: Map<Ternary> = {};
|
||||
|
||||
function isTypeIdenticalTo(source: Type, target: Type): boolean {
|
||||
return checkTypeRelatedTo(source, target, identityRelation, /*errorNode*/ undefined);
|
||||
}
|
||||
|
||||
function compareTypes(source: Type, target: Type): number {
|
||||
return checkTypeRelatedTo(source, target, identityRelation, /*errorNode*/ undefined) ? -1 : 0;
|
||||
}
|
||||
|
||||
function isTypeSubtypeOf(source: Type, target: Type): boolean {
|
||||
return checkTypeSubtypeOf(source, target, /*errorNode*/ undefined);
|
||||
}
|
||||
|
||||
function checkTypeSubtypeOf(
|
||||
source: Type,
|
||||
target: Type,
|
||||
errorNode: Node,
|
||||
headMessage?: DiagnosticMessage,
|
||||
containingMessageChain?: DiagnosticMessageChain): boolean {
|
||||
|
||||
return checkTypeRelatedTo(source, target, subtypeRelation, errorNode, headMessage, containingMessageChain);
|
||||
}
|
||||
|
||||
function isTypeAssignableTo(source: Type, target: Type): boolean {
|
||||
return checkTypeAssignableTo(source, target, /*errorNode*/ undefined);
|
||||
}
|
||||
|
||||
function checkTypeAssignableTo(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage): boolean {
|
||||
return checkTypeRelatedTo(source, target, assignableRelation, errorNode, headMessage);
|
||||
function checkTypeSubtypeOf(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: DiagnosticMessageChain): boolean {
|
||||
return checkTypeRelatedTo(source, target, subtypeRelation, errorNode, headMessage, containingMessageChain);
|
||||
}
|
||||
|
||||
function isTypeRelatedTo(source: Type, target: Type, relation: Map<boolean>): boolean {
|
||||
return checkTypeRelatedTo(source, target, relation, /*errorNode*/ undefined);
|
||||
function checkTypeAssignableTo(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage): boolean {
|
||||
return checkTypeRelatedTo(source, target, assignableRelation, errorNode, headMessage);
|
||||
}
|
||||
|
||||
function isSignatureAssignableTo(source: Signature, target: Signature): boolean {
|
||||
@@ -3249,71 +3243,10 @@ module ts {
|
||||
return checkTypeRelatedTo(sourceType, targetType, assignableRelation, /*errorNode*/ undefined);
|
||||
}
|
||||
|
||||
function isPropertyIdenticalTo(sourceProp: Symbol, targetProp: Symbol): boolean {
|
||||
return isPropertyIdenticalToRecursive(sourceProp, targetProp, /*reportErrors*/ false, (s, t, _reportErrors) => isTypeIdenticalTo(s, t));
|
||||
}
|
||||
|
||||
function checkInheritedPropertiesAreIdentical(type: InterfaceType, typeNode: Node): boolean {
|
||||
if (!type.baseTypes.length || type.baseTypes.length === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var seen: Map<{ prop: Symbol; containingType: Type }> = {};
|
||||
forEach(type.declaredProperties, p => { seen[p.name] = { prop: p, containingType: type }; });
|
||||
var ok = true;
|
||||
|
||||
for (var i = 0, len = type.baseTypes.length; i < len; ++i) {
|
||||
var base = type.baseTypes[i];
|
||||
var properties = getPropertiesOfObjectType(base);
|
||||
for (var j = 0, proplen = properties.length; j < proplen; ++j) {
|
||||
var prop = properties[j];
|
||||
if (!hasProperty(seen, prop.name)) {
|
||||
seen[prop.name] = { prop: prop, containingType: base };
|
||||
}
|
||||
else {
|
||||
var existing = seen[prop.name];
|
||||
var isInheritedProperty = existing.containingType !== type;
|
||||
if (isInheritedProperty && !isPropertyIdenticalTo(existing.prop, prop)) {
|
||||
ok = false;
|
||||
|
||||
var typeName1 = typeToString(existing.containingType);
|
||||
var typeName2 = typeToString(base);
|
||||
|
||||
var errorInfo = chainDiagnosticMessages(undefined, Diagnostics.Named_properties_0_of_types_1_and_2_are_not_identical, prop.name, typeName1, typeName2);
|
||||
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Interface_0_cannot_simultaneously_extend_types_1_and_2, typeToString(type), typeName1, typeName2);
|
||||
addDiagnostic(createDiagnosticForNodeFromMessageChain(typeNode, errorInfo, program.getCompilerHost().getNewLine()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
function isPropertyIdenticalToRecursive(sourceProp: Symbol, targetProp: Symbol, reportErrors: boolean, relate: (source: Type, target: Type, reportErrors: boolean) => boolean): boolean {
|
||||
// Two members are considered identical when
|
||||
// - they are public properties with identical names, optionality, and types,
|
||||
// - they are private or protected properties originating in the same declaration and having identical types
|
||||
if (sourceProp === targetProp) {
|
||||
return true;
|
||||
}
|
||||
var sourcePropAccessibility = getDeclarationFlagsFromSymbol(sourceProp) & (NodeFlags.Private | NodeFlags.Protected);
|
||||
var targetPropAccessibility = getDeclarationFlagsFromSymbol(targetProp) & (NodeFlags.Private | NodeFlags.Protected);
|
||||
if (sourcePropAccessibility !== targetPropAccessibility) {
|
||||
return false;
|
||||
}
|
||||
if (sourcePropAccessibility) {
|
||||
return getTargetSymbol(sourceProp) === getTargetSymbol(targetProp) && relate(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp), reportErrors);
|
||||
}
|
||||
else {
|
||||
return isOptionalProperty(sourceProp) === isOptionalProperty(targetProp) && relate(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp), reportErrors);
|
||||
}
|
||||
}
|
||||
|
||||
function checkTypeRelatedTo(
|
||||
source: Type,
|
||||
target: Type,
|
||||
relation: Map<boolean>,
|
||||
relation: Map<Ternary>,
|
||||
errorNode: Node,
|
||||
headMessage?: DiagnosticMessage,
|
||||
containingMessageChain?: DiagnosticMessageChain): boolean {
|
||||
@@ -3327,7 +3260,7 @@ module ts {
|
||||
|
||||
Debug.assert(relation !== identityRelation || !errorNode, "no error reporting in identity checking");
|
||||
|
||||
var result = isRelatedToWithCustomErrors(source, target, errorNode !== undefined, headMessage);
|
||||
var result = isRelatedTo(source, target, errorNode !== undefined, headMessage);
|
||||
if (overflow) {
|
||||
error(errorNode, Diagnostics.Excessive_stack_depth_comparing_types_0_and_1, typeToString(source), typeToString(target));
|
||||
}
|
||||
@@ -3337,54 +3270,55 @@ module ts {
|
||||
}
|
||||
addDiagnostic(createDiagnosticForNodeFromMessageChain(errorNode, errorInfo, program.getCompilerHost().getNewLine()));
|
||||
}
|
||||
return result;
|
||||
return result !== 0;
|
||||
|
||||
function reportError(message: DiagnosticMessage, arg0?: string, arg1?: string, arg2?: string): void {
|
||||
errorInfo = chainDiagnosticMessages(errorInfo, message, arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
function isRelatedTo(source: Type, target: Type, reportErrors?: boolean): boolean {
|
||||
return isRelatedToWithCustomErrors(source, target, reportErrors, /*headMessage*/ undefined);
|
||||
}
|
||||
|
||||
function isRelatedToWithCustomErrors(source: Type, target: Type, reportErrors: boolean, headMessage: DiagnosticMessage): boolean {
|
||||
// Compare two types and return
|
||||
// Ternary.True if they are related with no assumptions,
|
||||
// Ternary.Maybe if they are related with assumptions of other relationships, or
|
||||
// Ternary.False if they are not related.
|
||||
function isRelatedTo(source: Type, target: Type, reportErrors?: boolean, headMessage?: DiagnosticMessage): Ternary {
|
||||
var result: Ternary;
|
||||
if (relation === identityRelation) {
|
||||
// both types are the same - covers 'they are the same primitive type or both are Any' or the same type parameter cases
|
||||
if (source === target) return true;
|
||||
if (source === target) return Ternary.True;
|
||||
}
|
||||
else {
|
||||
if (source === target) return true;
|
||||
if (target.flags & TypeFlags.Any) return true;
|
||||
if (source === undefinedType) return true;
|
||||
if (source === nullType && target !== undefinedType) return true;
|
||||
if (source.flags & TypeFlags.Enum && target === numberType) return true;
|
||||
if (source.flags & TypeFlags.StringLiteral && target === stringType) return true;
|
||||
if (source === target) return Ternary.True;
|
||||
if (target.flags & TypeFlags.Any) return Ternary.True;
|
||||
if (source === undefinedType) return Ternary.True;
|
||||
if (source === nullType && target !== undefinedType) return Ternary.True;
|
||||
if (source.flags & TypeFlags.Enum && target === numberType) return Ternary.True;
|
||||
if (source.flags & TypeFlags.StringLiteral && target === stringType) return Ternary.True;
|
||||
if (relation === assignableRelation) {
|
||||
if (source.flags & TypeFlags.Any) return true;
|
||||
if (source === numberType && target.flags & TypeFlags.Enum) return true;
|
||||
if (source.flags & TypeFlags.Any) return Ternary.True;
|
||||
if (source === numberType && target.flags & TypeFlags.Enum) return Ternary.True;
|
||||
}
|
||||
}
|
||||
if (source.flags & TypeFlags.Union) {
|
||||
if (unionTypeRelatedToType(<UnionType>source, target, reportErrors)) {
|
||||
return true;
|
||||
if (result = unionTypeRelatedToType(<UnionType>source, target, reportErrors)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else if (target.flags & TypeFlags.Union) {
|
||||
if (typeRelatedToUnionType(source, <UnionType>target, reportErrors)) {
|
||||
return true;
|
||||
if (result = typeRelatedToUnionType(source, <UnionType>target, reportErrors)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else if (source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.TypeParameter) {
|
||||
if (typeParameterRelatedTo(<TypeParameter>source, <TypeParameter>target, reportErrors)) {
|
||||
return true;
|
||||
if (result = typeParameterRelatedTo(<TypeParameter>source, <TypeParameter>target, reportErrors)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
var saveErrorInfo = errorInfo;
|
||||
if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
|
||||
// We have type references to same target type, see if relationship holds for all type arguments
|
||||
if (typesRelatedTo((<TypeReference>source).typeArguments, (<TypeReference>target).typeArguments, reportErrors)) {
|
||||
return true;
|
||||
if (result = typesRelatedTo((<TypeReference>source).typeArguments, (<TypeReference>target).typeArguments, reportErrors)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// Even if relationship doesn't hold for type arguments, it may hold in a structural comparison
|
||||
@@ -3393,9 +3327,9 @@ module ts {
|
||||
// identity relation does not use apparent type
|
||||
var sourceOrApparentType = relation === identityRelation ? source : getApparentType(source);
|
||||
if (sourceOrApparentType.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType &&
|
||||
objectTypeRelatedTo(sourceOrApparentType, <ObjectType>target, reportStructuralErrors)) {
|
||||
(result = objectTypeRelatedTo(sourceOrApparentType, <ObjectType>target, reportStructuralErrors))) {
|
||||
errorInfo = saveErrorInfo;
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (reportErrors) {
|
||||
@@ -3403,61 +3337,67 @@ module ts {
|
||||
Debug.assert(headMessage);
|
||||
reportError(headMessage, typeToString(source), typeToString(target));
|
||||
}
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
|
||||
function typeRelatedToUnionType(source: Type, target: UnionType, reportErrors: boolean): boolean {
|
||||
function typeRelatedToUnionType(source: Type, target: UnionType, reportErrors: boolean): Ternary {
|
||||
var targetTypes = target.types;
|
||||
for (var i = 0, len = targetTypes.length; i < len; i++) {
|
||||
if (isRelatedTo(source, targetTypes[i], reportErrors && i === len - 1)) {
|
||||
return true;
|
||||
var related = isRelatedTo(source, targetTypes[i], reportErrors && i === len - 1);
|
||||
if (related) {
|
||||
return related;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
|
||||
function unionTypeRelatedToType(source: UnionType, target: Type, reportErrors: boolean): boolean {
|
||||
function unionTypeRelatedToType(source: UnionType, target: Type, reportErrors: boolean): Ternary {
|
||||
var result = Ternary.True;
|
||||
var sourceTypes = source.types;
|
||||
for (var i = 0, len = sourceTypes.length; i < len; i++) {
|
||||
if (!isRelatedTo(sourceTypes[i], target, reportErrors)) {
|
||||
return false;
|
||||
var related = isRelatedTo(sourceTypes[i], target, reportErrors);
|
||||
if (!related) {
|
||||
return Ternary.False;
|
||||
}
|
||||
result &= related;
|
||||
}
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
||||
function typesRelatedTo(sources: Type[], targets: Type[], reportErrors: boolean): boolean {
|
||||
function typesRelatedTo(sources: Type[], targets: Type[], reportErrors: boolean): Ternary {
|
||||
var result = Ternary.True;
|
||||
for (var i = 0, len = sources.length; i < len; i++) {
|
||||
if (!isRelatedTo(sources[i], targets[i], reportErrors)) return false;
|
||||
var related = isRelatedTo(sources[i], targets[i], reportErrors);
|
||||
if (!related) {
|
||||
return Ternary.False;
|
||||
}
|
||||
result &= related;
|
||||
}
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
||||
function typeParameterRelatedTo(source: TypeParameter, target: TypeParameter, reportErrors: boolean): boolean {
|
||||
function typeParameterRelatedTo(source: TypeParameter, target: TypeParameter, reportErrors: boolean): Ternary {
|
||||
if (relation === identityRelation) {
|
||||
if (source.symbol.name !== target.symbol.name) {
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
|
||||
// covers case when both type parameters does not have constraint (both equal to noConstraintType)
|
||||
if (source.constraint === target.constraint) {
|
||||
return true;
|
||||
return Ternary.True;
|
||||
}
|
||||
|
||||
if (source.constraint === noConstraintType || target.constraint === noConstraintType) {
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
|
||||
return isRelatedTo(source.constraint, target.constraint, reportErrors);
|
||||
}
|
||||
else {
|
||||
while (true) {
|
||||
var constraint = getConstraintOfTypeParameter(source);
|
||||
if (constraint === target) return true;
|
||||
if (constraint === target) return Ternary.True;
|
||||
if (!(constraint && constraint.flags & TypeFlags.TypeParameter)) break;
|
||||
source = <TypeParameter>constraint;
|
||||
}
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3466,18 +3406,23 @@ module ts {
|
||||
// Third, check if both types are part of deeply nested chains of generic type instantiations and if so assume the types are
|
||||
// equal and infinitely expanding. Fourth, if we have reached a depth of 100 nested comparisons, assume we have runaway recursion
|
||||
// and issue an error. Otherwise, actually compare the structure of the two types.
|
||||
function objectTypeRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): boolean {
|
||||
if (overflow) return false;
|
||||
var result: boolean;
|
||||
function objectTypeRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): Ternary {
|
||||
if (overflow) {
|
||||
return Ternary.False;
|
||||
}
|
||||
var id = source.id + "," + target.id;
|
||||
if ((result = relation[id]) !== undefined) return result;
|
||||
var related = relation[id];
|
||||
if (related !== undefined) {
|
||||
return related;
|
||||
}
|
||||
if (depth > 0) {
|
||||
for (var i = 0; i < depth; i++) {
|
||||
if (source === sourceStack[i] && target === targetStack[i]) return true;
|
||||
// If source and target are already being compared, consider them related with assumptions
|
||||
if (source === sourceStack[i] && target === targetStack[i]) return Ternary.Maybe;
|
||||
}
|
||||
if (depth === 100) {
|
||||
overflow = true;
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -3491,15 +3436,28 @@ module ts {
|
||||
var saveExpandingFlags = expandingFlags;
|
||||
if (!(expandingFlags & 1) && isDeeplyNestedGeneric(source, sourceStack)) expandingFlags |= 1;
|
||||
if (!(expandingFlags & 2) && isDeeplyNestedGeneric(target, targetStack)) expandingFlags |= 2;
|
||||
result = expandingFlags === 3 ||
|
||||
propertiesRelatedTo(source, target, reportErrors) &&
|
||||
signaturesRelatedTo(source, target, SignatureKind.Call, reportErrors) &&
|
||||
signaturesRelatedTo(source, target, SignatureKind.Construct, reportErrors) &&
|
||||
stringIndexTypesRelatedTo(source, target, reportErrors) &&
|
||||
numberIndexTypesRelatedTo(source, target, reportErrors);
|
||||
if (expandingFlags === 3) {
|
||||
var result = Ternary.True;
|
||||
}
|
||||
else {
|
||||
var result = propertiesRelatedTo(source, target, reportErrors);
|
||||
if (result) {
|
||||
result &= signaturesRelatedTo(source, target, SignatureKind.Call, reportErrors);
|
||||
if (result) {
|
||||
result &= signaturesRelatedTo(source, target, SignatureKind.Construct, reportErrors);
|
||||
if (result) {
|
||||
result &= stringIndexTypesRelatedTo(source, target, reportErrors);
|
||||
if (result) {
|
||||
result &= numberIndexTypesRelatedTo(source, target, reportErrors);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
expandingFlags = saveExpandingFlags;
|
||||
depth--;
|
||||
if (depth === 0) {
|
||||
// Only cache results that are free of assumptions
|
||||
if (result !== Ternary.Maybe) {
|
||||
relation[id] = result;
|
||||
}
|
||||
return result;
|
||||
@@ -3525,10 +3483,11 @@ module ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
function propertiesRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): boolean {
|
||||
function propertiesRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): Ternary {
|
||||
if (relation === identityRelation) {
|
||||
return propertiesIdenticalTo(source, target, reportErrors);
|
||||
return propertiesIdenticalTo(source, target);
|
||||
}
|
||||
var result = Ternary.True;
|
||||
var properties = getPropertiesOfObjectType(target);
|
||||
for (var i = 0; i < properties.length; i++) {
|
||||
var targetProp = properties[i];
|
||||
@@ -3539,7 +3498,7 @@ module ts {
|
||||
if (reportErrors) {
|
||||
reportError(Diagnostics.Property_0_is_missing_in_type_1, symbolToString(targetProp), typeToString(source));
|
||||
}
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
}
|
||||
else if (!(targetProp.flags & SymbolFlags.Prototype)) {
|
||||
@@ -3557,7 +3516,7 @@ module ts {
|
||||
typeToString(sourceFlags & NodeFlags.Private ? target : source));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
}
|
||||
else if (targetFlags & NodeFlags.Protected) {
|
||||
@@ -3569,7 +3528,7 @@ module ts {
|
||||
reportError(Diagnostics.Property_0_is_protected_but_type_1_is_not_a_class_derived_from_2,
|
||||
symbolToString(targetProp), typeToString(sourceClass || source), typeToString(targetClass));
|
||||
}
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
}
|
||||
else if (sourceFlags & NodeFlags.Protected) {
|
||||
@@ -3577,14 +3536,16 @@ module ts {
|
||||
reportError(Diagnostics.Property_0_is_protected_in_type_1_but_public_in_type_2,
|
||||
symbolToString(targetProp), typeToString(source), typeToString(target));
|
||||
}
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
if (!isRelatedTo(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp), reportErrors)) {
|
||||
var related = isRelatedTo(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp), reportErrors);
|
||||
if (!related) {
|
||||
if (reportErrors) {
|
||||
reportError(Diagnostics.Types_of_property_0_are_incompatible, symbolToString(targetProp));
|
||||
}
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
result &= related;
|
||||
if (isOptionalProperty(sourceProp) && !isOptionalProperty(targetProp)) {
|
||||
// TypeScript 1.0 spec (April 2014): 3.8.3
|
||||
// S is a subtype of a type T, and T is a supertype of S if ...
|
||||
@@ -3597,37 +3558,46 @@ module ts {
|
||||
reportError(Diagnostics.Property_0_is_optional_in_type_1_but_required_in_type_2,
|
||||
symbolToString(targetProp), typeToString(source), typeToString(target));
|
||||
}
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
||||
function propertiesIdenticalTo(source: ObjectType, target: ObjectType, reportErrors: boolean): boolean {
|
||||
function propertiesIdenticalTo(source: ObjectType, target: ObjectType): Ternary {
|
||||
var sourceProperties = getPropertiesOfObjectType(source);
|
||||
var targetProperties = getPropertiesOfObjectType(target);
|
||||
if (sourceProperties.length !== targetProperties.length) {
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
var result = Ternary.True;
|
||||
for (var i = 0, len = sourceProperties.length; i < len; ++i) {
|
||||
var sourceProp = sourceProperties[i];
|
||||
var targetProp = getPropertyOfObjectType(target, sourceProp.name);
|
||||
if (!targetProp || !isPropertyIdenticalToRecursive(sourceProp, targetProp, reportErrors, isRelatedTo)) {
|
||||
return false;
|
||||
if (!targetProp) {
|
||||
return Ternary.False;
|
||||
}
|
||||
var related = compareProperties(sourceProp, targetProp, isRelatedTo);
|
||||
if (!related) {
|
||||
return Ternary.False;
|
||||
}
|
||||
result &= related;
|
||||
}
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
||||
function signaturesRelatedTo(source: ObjectType, target: ObjectType, kind: SignatureKind, reportErrors: boolean): boolean {
|
||||
function signaturesRelatedTo(source: ObjectType, target: ObjectType, kind: SignatureKind, reportErrors: boolean): Ternary {
|
||||
if (relation === identityRelation) {
|
||||
return signaturesIdenticalTo(source, target, kind, reportErrors);
|
||||
return signaturesIdenticalTo(source, target, kind);
|
||||
}
|
||||
if (target === anyFunctionType || source === anyFunctionType) {
|
||||
return Ternary.True;
|
||||
}
|
||||
if (target === anyFunctionType || source === anyFunctionType) return true;
|
||||
var sourceSignatures = getSignaturesOfType(source, kind);
|
||||
var targetSignatures = getSignaturesOfType(target, kind);
|
||||
var result = Ternary.True;
|
||||
var saveErrorInfo = errorInfo;
|
||||
outer: for (var i = 0; i < targetSignatures.length; i++) {
|
||||
var t = targetSignatures[i];
|
||||
@@ -3636,7 +3606,9 @@ module ts {
|
||||
for (var j = 0; j < sourceSignatures.length; j++) {
|
||||
var s = sourceSignatures[j];
|
||||
if (!s.hasStringLiterals || source.flags & TypeFlags.FromSignature) {
|
||||
if (signatureRelatedTo(s, t, localErrors)) {
|
||||
var related = signatureRelatedTo(s, t, localErrors);
|
||||
if (related) {
|
||||
result &= related;
|
||||
errorInfo = saveErrorInfo;
|
||||
continue outer;
|
||||
}
|
||||
@@ -3644,18 +3616,18 @@ module ts {
|
||||
localErrors = false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
||||
function signatureRelatedTo(source: Signature, target: Signature, reportErrors: boolean): boolean {
|
||||
function signatureRelatedTo(source: Signature, target: Signature, reportErrors: boolean): Ternary {
|
||||
if (source === target) {
|
||||
return true;
|
||||
return Ternary.True;
|
||||
}
|
||||
if (!target.hasRestParameter && source.minArgumentCount > target.parameters.length) {
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
var sourceMax = source.parameters.length;
|
||||
var targetMax = target.parameters.length;
|
||||
@@ -3680,45 +3652,52 @@ module ts {
|
||||
// M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N
|
||||
source = getErasedSignature(source);
|
||||
target = getErasedSignature(target);
|
||||
var result = Ternary.True;
|
||||
for (var i = 0; i < checkCount; i++) {
|
||||
var s = i < sourceMax ? getTypeOfSymbol(source.parameters[i]) : getRestTypeOfSignature(source);
|
||||
var t = i < targetMax ? getTypeOfSymbol(target.parameters[i]) : getRestTypeOfSignature(target);
|
||||
var saveErrorInfo = errorInfo;
|
||||
if (!isRelatedTo(s, t, reportErrors)) {
|
||||
if (!isRelatedTo(t, s, false)) {
|
||||
var related = isRelatedTo(s, t, reportErrors);
|
||||
if (!related) {
|
||||
related = isRelatedTo(t, s, false);
|
||||
if (!related) {
|
||||
if (reportErrors) {
|
||||
reportError(Diagnostics.Types_of_parameters_0_and_1_are_incompatible,
|
||||
source.parameters[i < sourceMax ? i : sourceMax].name,
|
||||
target.parameters[i < targetMax ? i : targetMax].name);
|
||||
}
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
errorInfo = saveErrorInfo;
|
||||
}
|
||||
result &= related;
|
||||
}
|
||||
var t = getReturnTypeOfSignature(target);
|
||||
if (t === voidType) return true;
|
||||
if (t === voidType) return result;
|
||||
var s = getReturnTypeOfSignature(source);
|
||||
return isRelatedTo(s, t, reportErrors);
|
||||
return result & isRelatedTo(s, t, reportErrors);
|
||||
}
|
||||
|
||||
function signaturesIdenticalTo(source: ObjectType, target: ObjectType, kind: SignatureKind, reportErrors: boolean): boolean {
|
||||
function signaturesIdenticalTo(source: ObjectType, target: ObjectType, kind: SignatureKind): Ternary {
|
||||
var sourceSignatures = getSignaturesOfType(source, kind);
|
||||
var targetSignatures = getSignaturesOfType(target, kind);
|
||||
if (sourceSignatures.length !== targetSignatures.length) {
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
var result = Ternary.True;
|
||||
for (var i = 0, len = sourceSignatures.length; i < len; ++i) {
|
||||
if (!compareSignatures(sourceSignatures[i], targetSignatures[i], /*compareReturnTypes*/ true, isRelatedTo)) {
|
||||
return false;
|
||||
var related = compareSignatures(sourceSignatures[i], targetSignatures[i], /*compareReturnTypes*/ true, isRelatedTo);
|
||||
if (!related) {
|
||||
return Ternary.False;
|
||||
}
|
||||
result &= related;
|
||||
}
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
||||
function stringIndexTypesRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): boolean {
|
||||
function stringIndexTypesRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): Ternary {
|
||||
if (relation === identityRelation) {
|
||||
return indexTypesIdenticalTo(IndexKind.String, source, target, reportErrors);
|
||||
return indexTypesIdenticalTo(IndexKind.String, source, target);
|
||||
}
|
||||
var targetType = getIndexTypeOfType(target, IndexKind.String);
|
||||
if (targetType) {
|
||||
@@ -3727,21 +3706,23 @@ module ts {
|
||||
if (reportErrors) {
|
||||
reportError(Diagnostics.Index_signature_is_missing_in_type_0, typeToString(source));
|
||||
}
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
if (!isRelatedTo(sourceType, targetType, reportErrors)) {
|
||||
var related = isRelatedTo(sourceType, targetType, reportErrors);
|
||||
if (!related) {
|
||||
if (reportErrors) {
|
||||
reportError(Diagnostics.Index_signatures_are_incompatible);
|
||||
}
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
return related;
|
||||
}
|
||||
return true;
|
||||
return Ternary.True;
|
||||
}
|
||||
|
||||
function numberIndexTypesRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): boolean {
|
||||
function numberIndexTypesRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): Ternary {
|
||||
if (relation === identityRelation) {
|
||||
return indexTypesIdenticalTo(IndexKind.Number, source, target, reportErrors);
|
||||
return indexTypesIdenticalTo(IndexKind.Number, source, target);
|
||||
}
|
||||
var targetType = getIndexTypeOfType(target, IndexKind.Number);
|
||||
if (targetType) {
|
||||
@@ -3751,53 +3732,93 @@ module ts {
|
||||
if (reportErrors) {
|
||||
reportError(Diagnostics.Index_signature_is_missing_in_type_0, typeToString(source));
|
||||
}
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
if (sourceStringType && sourceNumberType) {
|
||||
// If we know for sure we're testing both string and numeric index types then only report errors from the second one
|
||||
var compatible = isRelatedTo(sourceStringType, targetType, false) || isRelatedTo(sourceNumberType, targetType, reportErrors);
|
||||
var related = isRelatedTo(sourceStringType, targetType, false) || isRelatedTo(sourceNumberType, targetType, reportErrors);
|
||||
}
|
||||
else {
|
||||
var compatible = isRelatedTo(sourceStringType || sourceNumberType, targetType, reportErrors);
|
||||
var related = isRelatedTo(sourceStringType || sourceNumberType, targetType, reportErrors);
|
||||
}
|
||||
if (!compatible) {
|
||||
if (!related) {
|
||||
if (reportErrors) {
|
||||
reportError(Diagnostics.Index_signatures_are_incompatible);
|
||||
}
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
return related;
|
||||
}
|
||||
return true;
|
||||
return Ternary.True;
|
||||
}
|
||||
|
||||
function indexTypesIdenticalTo(indexKind: IndexKind, source: ObjectType, target: ObjectType, reportErrors: boolean): boolean {
|
||||
function indexTypesIdenticalTo(indexKind: IndexKind, source: ObjectType, target: ObjectType): Ternary {
|
||||
var targetType = getIndexTypeOfType(target, indexKind);
|
||||
var sourceType = getIndexTypeOfType(source, indexKind);
|
||||
return (!sourceType && !targetType) || (sourceType && targetType && isRelatedTo(sourceType, targetType, reportErrors));
|
||||
if (!sourceType && !targetType) {
|
||||
return Ternary.True;
|
||||
}
|
||||
if (sourceType && targetType) {
|
||||
return isRelatedTo(sourceType, targetType);
|
||||
}
|
||||
return Ternary.False;
|
||||
}
|
||||
}
|
||||
|
||||
function compareSignatures(source: Signature, target: Signature, compareReturnTypes: boolean, compareTypes: (s: Type, t: Type) => boolean): boolean {
|
||||
function isPropertyIdenticalTo(sourceProp: Symbol, targetProp: Symbol): boolean {
|
||||
return compareProperties(sourceProp, targetProp, compareTypes) !== 0;
|
||||
}
|
||||
|
||||
function compareProperties(sourceProp: Symbol, targetProp: Symbol, compareTypes: (source: Type, target: Type) => Ternary): Ternary {
|
||||
// Two members are considered identical when
|
||||
// - they are public properties with identical names, optionality, and types,
|
||||
// - they are private or protected properties originating in the same declaration and having identical types
|
||||
if (sourceProp === targetProp) {
|
||||
return Ternary.True;
|
||||
}
|
||||
var sourcePropAccessibility = getDeclarationFlagsFromSymbol(sourceProp) & (NodeFlags.Private | NodeFlags.Protected);
|
||||
var targetPropAccessibility = getDeclarationFlagsFromSymbol(targetProp) & (NodeFlags.Private | NodeFlags.Protected);
|
||||
if (sourcePropAccessibility !== targetPropAccessibility) {
|
||||
return Ternary.False;
|
||||
}
|
||||
if (sourcePropAccessibility) {
|
||||
if (getTargetSymbol(sourceProp) !== getTargetSymbol(targetProp)) {
|
||||
return Ternary.False;
|
||||
}
|
||||
return compareTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp));
|
||||
}
|
||||
else {
|
||||
if (isOptionalProperty(sourceProp) !== isOptionalProperty(targetProp)) {
|
||||
return Ternary.False;
|
||||
}
|
||||
return compareTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp));
|
||||
}
|
||||
}
|
||||
|
||||
function compareSignatures(source: Signature, target: Signature, compareReturnTypes: boolean, compareTypes: (s: Type, t: Type) => Ternary): Ternary {
|
||||
if (source === target) {
|
||||
return true;
|
||||
return Ternary.True;
|
||||
}
|
||||
if (source.parameters.length !== target.parameters.length ||
|
||||
source.minArgumentCount !== target.minArgumentCount ||
|
||||
source.hasRestParameter !== target.hasRestParameter) {
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
var result = Ternary.True;
|
||||
if (source.typeParameters && target.typeParameters) {
|
||||
if (source.typeParameters.length !== target.typeParameters.length) {
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
for (var i = 0, len = source.typeParameters.length; i < len; ++i) {
|
||||
if (!compareTypes(source.typeParameters[i], target.typeParameters[i])) {
|
||||
return false;
|
||||
var related = compareTypes(source.typeParameters[i], target.typeParameters[i]);
|
||||
if (!related) {
|
||||
return Ternary.False;
|
||||
}
|
||||
result &= related;
|
||||
}
|
||||
}
|
||||
else if (source.typeParameters || source.typeParameters) {
|
||||
return false;
|
||||
return Ternary.False;
|
||||
}
|
||||
// Spec 1.0 Section 3.8.3 & 3.8.4:
|
||||
// M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N
|
||||
@@ -3806,11 +3827,16 @@ module ts {
|
||||
for (var i = 0, len = source.parameters.length; i < len; i++) {
|
||||
var s = source.hasRestParameter && i === len - 1 ? getRestTypeOfSignature(source) : getTypeOfSymbol(source.parameters[i]);
|
||||
var t = target.hasRestParameter && i === len - 1 ? getRestTypeOfSignature(target) : getTypeOfSymbol(target.parameters[i]);
|
||||
if (!compareTypes(s, t)) {
|
||||
return false;
|
||||
var related = compareTypes(s, t);
|
||||
if (!related) {
|
||||
return Ternary.False;
|
||||
}
|
||||
result &= related;
|
||||
}
|
||||
return !compareReturnTypes || compareTypes(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target));
|
||||
if (compareReturnTypes) {
|
||||
result &= compareTypes(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function isSupertypeOfEach(candidate: Type, types: Type[]): boolean {
|
||||
@@ -4859,7 +4885,7 @@ module ts {
|
||||
if (!result) {
|
||||
result = signature;
|
||||
}
|
||||
else if (!compareSignatures(result, signature, /*compareReturnTypes*/ true, isTypeIdenticalTo)) {
|
||||
else if (!compareSignatures(result, signature, /*compareReturnTypes*/ true, compareTypes)) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -5251,7 +5277,7 @@ module ts {
|
||||
return typeArgumentsAreAssignable;
|
||||
}
|
||||
|
||||
function checkApplicableSignature(node: CallExpression, signature: Signature, relation: Map<boolean>, excludeArgument: boolean[], reportErrors: boolean) {
|
||||
function checkApplicableSignature(node: CallExpression, signature: Signature, relation: Map<Ternary>, excludeArgument: boolean[], reportErrors: boolean) {
|
||||
if (node.arguments) {
|
||||
for (var i = 0; i < node.arguments.length; i++) {
|
||||
var arg = node.arguments[i];
|
||||
@@ -5389,7 +5415,7 @@ module ts {
|
||||
|
||||
return resolveErrorCall(node);
|
||||
|
||||
function chooseOverload(candidates: Signature[], relation: Map<boolean>, excludeArgument: boolean[]) {
|
||||
function chooseOverload(candidates: Signature[], relation: Map<Ternary>, excludeArgument: boolean[]) {
|
||||
for (var i = 0; i < candidates.length; i++) {
|
||||
if (!signatureHasCorrectArity(node, candidates[i])) {
|
||||
continue;
|
||||
@@ -7574,6 +7600,43 @@ module ts {
|
||||
return true;
|
||||
}
|
||||
|
||||
function checkInheritedPropertiesAreIdentical(type: InterfaceType, typeNode: Node): boolean {
|
||||
if (!type.baseTypes.length || type.baseTypes.length === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var seen: Map<{ prop: Symbol; containingType: Type }> = {};
|
||||
forEach(type.declaredProperties, p => { seen[p.name] = { prop: p, containingType: type }; });
|
||||
var ok = true;
|
||||
|
||||
for (var i = 0, len = type.baseTypes.length; i < len; ++i) {
|
||||
var base = type.baseTypes[i];
|
||||
var properties = getPropertiesOfObjectType(base);
|
||||
for (var j = 0, proplen = properties.length; j < proplen; ++j) {
|
||||
var prop = properties[j];
|
||||
if (!hasProperty(seen, prop.name)) {
|
||||
seen[prop.name] = { prop: prop, containingType: base };
|
||||
}
|
||||
else {
|
||||
var existing = seen[prop.name];
|
||||
var isInheritedProperty = existing.containingType !== type;
|
||||
if (isInheritedProperty && !isPropertyIdenticalTo(existing.prop, prop)) {
|
||||
ok = false;
|
||||
|
||||
var typeName1 = typeToString(existing.containingType);
|
||||
var typeName2 = typeToString(base);
|
||||
|
||||
var errorInfo = chainDiagnosticMessages(undefined, Diagnostics.Named_properties_0_of_types_1_and_2_are_not_identical, prop.name, typeName1, typeName2);
|
||||
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Interface_0_cannot_simultaneously_extend_types_1_and_2, typeToString(type), typeName1, typeName2);
|
||||
addDiagnostic(createDiagnosticForNodeFromMessageChain(typeNode, errorInfo, program.getCompilerHost().getNewLine()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
function checkInterfaceDeclaration(node: InterfaceDeclaration) {
|
||||
checkTypeParameters(node.typeParameters);
|
||||
if (fullTypeCheck) {
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
/// <reference path="types.ts"/>
|
||||
|
||||
module ts {
|
||||
|
||||
// Ternary values are defined such that
|
||||
// x & y is False if either x or y is False.
|
||||
// x & y is Maybe if either x or y is Maybe, but neither x or y is False.
|
||||
// x & y is True if x and y are both True.
|
||||
export enum Ternary {
|
||||
False = 0,
|
||||
Maybe = 1,
|
||||
True = -1
|
||||
}
|
||||
|
||||
export interface Map<T> {
|
||||
[index: string]: T;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user