From 3b1c833d67450c86391c0e6b3d4de1b96ea570d4 Mon Sep 17 00:00:00 2001 From: Jason Freeman Date: Thu, 16 Apr 2015 15:07:48 -0700 Subject: [PATCH 1/5] Lazily compute base types --- src/compiler/checker.ts | 138 +++++++++++++++++++++++----------------- src/compiler/types.ts | 5 +- 2 files changed, 85 insertions(+), 58 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d0c51eb05c..f9a6d515d1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2423,7 +2423,7 @@ module ts { return check(type); function check(type: InterfaceType): boolean { let target = getTargetType(type); - return target === checkBase || forEach(target.baseTypes, check); + return target === checkBase || forEach(getBaseTypes(target), check); } } @@ -2451,6 +2451,69 @@ module ts { return result; } + function getBaseTypes(type: InterfaceType): ObjectType[] { + if (!(type).baseTypes) { + if (type.symbol.flags & SymbolFlags.Class) { + resolveBaseTypesOfClass(type); + } + else if (type.symbol.flags & SymbolFlags.Interface) { + resolveBaseTypesOfInterface(type); + } + else { + Debug.fail("type must be class or interface"); + } + } + + return (type).baseTypes; + } + + function resolveBaseTypesOfClass(type: InterfaceTypeWithBaseTypes): void { + type.baseTypes = []; + let declaration = getDeclarationOfKind(type.symbol, SyntaxKind.ClassDeclaration); + let baseTypeNode = getClassExtendsHeritageClauseElement(declaration); + if (baseTypeNode) { + let baseType = getTypeFromHeritageClauseElement(baseTypeNode); + if (baseType !== unknownType) { + if (getTargetType(baseType).flags & TypeFlags.Class) { + if (type !== baseType && !hasBaseType(baseType, type)) { + type.baseTypes.push(baseType); + } + else { + error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); + } + } + else { + error(baseTypeNode, Diagnostics.A_class_may_only_extend_another_class); + } + } + } + } + + function resolveBaseTypesOfInterface(type: InterfaceTypeWithBaseTypes): void { + type.baseTypes = []; + forEach(type.symbol.declarations, declaration => { + if (declaration.kind === SyntaxKind.InterfaceDeclaration && getInterfaceBaseTypeNodes(declaration)) { + forEach(getInterfaceBaseTypeNodes(declaration), node => { + let baseType = getTypeFromHeritageClauseElement(node); + + if (baseType !== unknownType) { + if (getTargetType(baseType).flags & (TypeFlags.Class | TypeFlags.Interface)) { + if (type !== baseType && !hasBaseType(baseType, type)) { + type.baseTypes.push(baseType); + } + else { + error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); + } + } + else { + error(node, Diagnostics.An_interface_may_only_extend_a_class_or_another_interface); + } + } + }); + } + }); + } + function getDeclaredTypeOfClass(symbol: Symbol): InterfaceType { let links = getSymbolLinks(symbol); if (!links.declaredType) { @@ -2464,25 +2527,7 @@ module ts { (type).target = type; (type).typeArguments = type.typeParameters; } - type.baseTypes = []; - let declaration = getDeclarationOfKind(symbol, SyntaxKind.ClassDeclaration); - let baseTypeNode = getClassExtendsHeritageClauseElement(declaration); - if (baseTypeNode) { - let baseType = getTypeFromHeritageClauseElement(baseTypeNode); - if (baseType !== unknownType) { - if (getTargetType(baseType).flags & TypeFlags.Class) { - if (type !== baseType && !hasBaseType(baseType, type)) { - type.baseTypes.push(baseType); - } - else { - error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); - } - } - else { - error(baseTypeNode, Diagnostics.A_class_may_only_extend_another_class); - } - } - } + type.declaredProperties = getNamedMembers(symbol.members); type.declaredCallSignatures = emptyArray; type.declaredConstructSignatures = emptyArray; @@ -2505,28 +2550,7 @@ module ts { (type).target = type; (type).typeArguments = type.typeParameters; } - type.baseTypes = []; - forEach(symbol.declarations, declaration => { - if (declaration.kind === SyntaxKind.InterfaceDeclaration && getInterfaceBaseTypeNodes(declaration)) { - forEach(getInterfaceBaseTypeNodes(declaration), node => { - let baseType = getTypeFromHeritageClauseElement(node); - if (baseType !== unknownType) { - if (getTargetType(baseType).flags & (TypeFlags.Class | TypeFlags.Interface)) { - if (type !== baseType && !hasBaseType(baseType, type)) { - type.baseTypes.push(baseType); - } - else { - error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); - } - } - else { - error(node, Diagnostics.An_interface_may_only_extend_a_class_or_another_interface); - } - } - }); - } - }); type.declaredProperties = getNamedMembers(symbol.members); type.declaredCallSignatures = getSignaturesOfSymbol(symbol.members["__call"]); type.declaredConstructSignatures = getSignaturesOfSymbol(symbol.members["__new"]); @@ -2646,9 +2670,9 @@ module ts { let constructSignatures = type.declaredConstructSignatures; let stringIndexType = type.declaredStringIndexType; let numberIndexType = type.declaredNumberIndexType; - if (type.baseTypes.length) { + if (getBaseTypes(type).length) { members = createSymbolTable(type.declaredProperties); - forEach(type.baseTypes, baseType => { + forEach(getBaseTypes(type), baseType => { addInheritedMembers(members, getPropertiesOfObjectType(baseType)); callSignatures = concatenate(callSignatures, getSignaturesOfType(baseType, SignatureKind.Call)); constructSignatures = concatenate(constructSignatures, getSignaturesOfType(baseType, SignatureKind.Construct)); @@ -2667,7 +2691,7 @@ module ts { let constructSignatures = instantiateList(target.declaredConstructSignatures, mapper, instantiateSignature); let stringIndexType = target.declaredStringIndexType ? instantiateType(target.declaredStringIndexType, mapper) : undefined; let numberIndexType = target.declaredNumberIndexType ? instantiateType(target.declaredNumberIndexType, mapper) : undefined; - forEach(target.baseTypes, baseType => { + forEach(getBaseTypes(target), baseType => { let instantiatedBaseType = instantiateType(baseType, mapper); addInheritedMembers(members, getPropertiesOfObjectType(instantiatedBaseType)); callSignatures = concatenate(callSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Call)); @@ -2697,8 +2721,8 @@ module ts { } function getDefaultConstructSignatures(classType: InterfaceType): Signature[] { - if (classType.baseTypes.length) { - let baseType = classType.baseTypes[0]; + if (getBaseTypes(classType).length) { + let baseType = getBaseTypes(classType)[0]; let baseSignatures = getSignaturesOfType(getTypeOfSymbol(baseType.symbol), SignatureKind.Construct); return map(baseSignatures, baseSignature => { let signature = baseType.flags & TypeFlags.Reference ? @@ -2820,9 +2844,9 @@ module ts { if (!constructSignatures.length) { constructSignatures = getDefaultConstructSignatures(classType); } - if (classType.baseTypes.length) { + if (getBaseTypes(classType).length) { members = createSymbolTable(getNamedMembers(members)); - addInheritedMembers(members, getPropertiesOfObjectType(getTypeOfSymbol(classType.baseTypes[0].symbol))); + addInheritedMembers(members, getPropertiesOfObjectType(getTypeOfSymbol(getBaseTypes(classType)[0].symbol))); } } stringIndexType = undefined; @@ -5550,7 +5574,7 @@ module ts { let baseClass: Type; if (enclosingClass && getClassExtendsHeritageClauseElement(enclosingClass)) { let classType = getDeclaredTypeOfSymbol(getSymbolOfNode(enclosingClass)); - baseClass = classType.baseTypes.length && classType.baseTypes[0]; + baseClass = getBaseTypes(classType).length && getBaseTypes(classType)[0]; } if (!baseClass) { @@ -9829,7 +9853,7 @@ module ts { errorNode = declaredNumberIndexer || declaredStringIndexer; // condition 'errorNode === undefined' may appear if types does not declare nor string neither number indexer if (!errorNode && (type.flags & TypeFlags.Interface)) { - let someBaseTypeHasBothIndexers = forEach((type).baseTypes, base => getIndexTypeOfType(base, IndexKind.String) && getIndexTypeOfType(base, IndexKind.Number)); + let someBaseTypeHasBothIndexers = forEach(getBaseTypes(type), base => getIndexTypeOfType(base, IndexKind.String) && getIndexTypeOfType(base, IndexKind.Number)); errorNode = someBaseTypeHasBothIndexers ? undefined : type.symbol.declarations[0]; } } @@ -9869,7 +9893,7 @@ module ts { // for interfaces property and indexer might be inherited from different bases // check if any base class already has both property and indexer. // check should be performed only if 'type' is the first type that brings property\indexer together - let someBaseClassHasBothPropertyAndIndexer = forEach((containingType).baseTypes, base => getPropertyOfObjectType(base, prop.name) && getIndexTypeOfType(base, indexKind)); + let someBaseClassHasBothPropertyAndIndexer = forEach(getBaseTypes(containingType), base => getPropertyOfObjectType(base, prop.name) && getIndexTypeOfType(base, indexKind)); errorNode = someBaseClassHasBothPropertyAndIndexer ? undefined : containingType.symbol.declarations[0]; } @@ -9953,9 +9977,9 @@ module ts { emitExtends = emitExtends || !isInAmbientContext(node); checkHeritageClauseElement(baseTypeNode); } - if (type.baseTypes.length) { + if (getBaseTypes(type).length) { if (produceDiagnostics) { - let baseType = type.baseTypes[0]; + let baseType = getBaseTypes(type)[0]; checkTypeAssignableTo(type, baseType, node.name || node, Diagnostics.Class_0_incorrectly_extends_base_class_1); let staticBaseType = getTypeOfSymbol(baseType.symbol); checkTypeAssignableTo(staticType, getTypeWithoutConstructors(staticBaseType), node.name || node, @@ -9969,7 +9993,7 @@ module ts { } } - if (type.baseTypes.length || (baseTypeNode && compilerOptions.separateCompilation)) { + if (getBaseTypes(type).length || (baseTypeNode && compilerOptions.separateCompilation)) { // Check that base type can be evaluated as expression checkExpressionOrQualifiedName(baseTypeNode.expression); } @@ -10113,7 +10137,7 @@ module ts { } function checkInheritedPropertiesAreIdentical(type: InterfaceType, typeNode: Node): boolean { - if (!type.baseTypes.length || type.baseTypes.length === 1) { + if (!getBaseTypes(type).length || getBaseTypes(type).length === 1) { return true; } @@ -10121,7 +10145,7 @@ module ts { forEach(type.declaredProperties, p => { seen[p.name] = { prop: p, containingType: type }; }); let ok = true; - for (let base of type.baseTypes) { + for (let base of getBaseTypes(type)) { let properties = getPropertiesOfObjectType(base); for (let prop of properties) { if (!hasProperty(seen, prop.name)) { @@ -10169,7 +10193,7 @@ module ts { let type = getDeclaredTypeOfSymbol(symbol); // run subsequent checks only if first set succeeded if (checkInheritedPropertiesAreIdentical(type, node.name)) { - forEach(type.baseTypes, baseType => { + forEach(getBaseTypes(type), baseType => { checkTypeAssignableTo(type, baseType, node.name, Diagnostics.Interface_0_incorrectly_extends_interface_1); }); checkIndexConstraints(type); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b3d2f7858e..67bdd8ea4f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1485,7 +1485,6 @@ module ts { // Class and interface types (TypeFlags.Class and TypeFlags.Interface) export interface InterfaceType extends ObjectType { typeParameters: TypeParameter[]; // Type parameters (undefined if non-generic) - baseTypes: ObjectType[]; // Base types declaredProperties: Symbol[]; // Declared members declaredCallSignatures: Signature[]; // Declared call signatures declaredConstructSignatures: Signature[]; // Declared construct signatures @@ -1493,6 +1492,10 @@ module ts { declaredNumberIndexType: Type; // Declared numeric index type } + export interface InterfaceTypeWithBaseTypes extends InterfaceType { + baseTypes: ObjectType[]; + } + // Type references (TypeFlags.Reference) export interface TypeReference extends ObjectType { target: GenericType; // Type reference target From 8bf012b566f1050631b58862000313ecb5c191fa Mon Sep 17 00:00:00 2001 From: Jason Freeman Date: Thu, 16 Apr 2015 18:14:25 -0700 Subject: [PATCH 2/5] Add tests for not depending on base types --- .../classDoesNotDependOnBaseTypes.js | 38 ++++++++++++++++ .../classDoesNotDependOnBaseTypes.types | 43 +++++++++++++++++++ .../interfaceDoesNotDependOnBaseTypes.js | 16 +++++++ .../interfaceDoesNotDependOnBaseTypes.types | 36 ++++++++++++++++ .../classDoesNotDependOnBaseTypes.ts | 12 ++++++ .../interfaceDoesNotDependOnBaseTypes.ts | 8 ++++ 6 files changed, 153 insertions(+) create mode 100644 tests/baselines/reference/classDoesNotDependOnBaseTypes.js create mode 100644 tests/baselines/reference/classDoesNotDependOnBaseTypes.types create mode 100644 tests/baselines/reference/interfaceDoesNotDependOnBaseTypes.js create mode 100644 tests/baselines/reference/interfaceDoesNotDependOnBaseTypes.types create mode 100644 tests/cases/conformance/types/typeAliases/classDoesNotDependOnBaseTypes.ts create mode 100644 tests/cases/conformance/types/typeAliases/interfaceDoesNotDependOnBaseTypes.ts diff --git a/tests/baselines/reference/classDoesNotDependOnBaseTypes.js b/tests/baselines/reference/classDoesNotDependOnBaseTypes.js new file mode 100644 index 0000000000..a62d5b1532 --- /dev/null +++ b/tests/baselines/reference/classDoesNotDependOnBaseTypes.js @@ -0,0 +1,38 @@ +//// [classDoesNotDependOnBaseTypes.ts] +var x: StringTree; +if (typeof x !== "string") { + x[0] = ""; + x[0] = new StringTreeCollection; +} + +type StringTree = string | StringTreeCollection; +class StringTreeCollectionBase { + [n: number]: StringTree; +} + +class StringTreeCollection extends StringTreeCollectionBase { } + +//// [classDoesNotDependOnBaseTypes.js] +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var x; +if (typeof x !== "string") { + x[0] = ""; + x[0] = new StringTreeCollection; +} +var StringTreeCollectionBase = (function () { + function StringTreeCollectionBase() { + } + return StringTreeCollectionBase; +})(); +var StringTreeCollection = (function (_super) { + __extends(StringTreeCollection, _super); + function StringTreeCollection() { + _super.apply(this, arguments); + } + return StringTreeCollection; +})(StringTreeCollectionBase); diff --git a/tests/baselines/reference/classDoesNotDependOnBaseTypes.types b/tests/baselines/reference/classDoesNotDependOnBaseTypes.types new file mode 100644 index 0000000000..c342f0ea00 --- /dev/null +++ b/tests/baselines/reference/classDoesNotDependOnBaseTypes.types @@ -0,0 +1,43 @@ +=== tests/cases/conformance/types/typeAliases/classDoesNotDependOnBaseTypes.ts === +var x: StringTree; +>x : string | StringTreeCollection +>StringTree : string | StringTreeCollection + +if (typeof x !== "string") { +>typeof x !== "string" : boolean +>typeof x : string +>x : string | StringTreeCollection +>"string" : string + + x[0] = ""; +>x[0] = "" : string +>x[0] : string | StringTreeCollection +>x : StringTreeCollection +>0 : number +>"" : string + + x[0] = new StringTreeCollection; +>x[0] = new StringTreeCollection : StringTreeCollection +>x[0] : string | StringTreeCollection +>x : StringTreeCollection +>0 : number +>new StringTreeCollection : StringTreeCollection +>StringTreeCollection : typeof StringTreeCollection +} + +type StringTree = string | StringTreeCollection; +>StringTree : string | StringTreeCollection +>StringTreeCollection : StringTreeCollection + +class StringTreeCollectionBase { +>StringTreeCollectionBase : StringTreeCollectionBase + + [n: number]: StringTree; +>n : number +>StringTree : string | StringTreeCollection +} + +class StringTreeCollection extends StringTreeCollectionBase { } +>StringTreeCollection : StringTreeCollection +>StringTreeCollectionBase : StringTreeCollectionBase + diff --git a/tests/baselines/reference/interfaceDoesNotDependOnBaseTypes.js b/tests/baselines/reference/interfaceDoesNotDependOnBaseTypes.js new file mode 100644 index 0000000000..4d8a23ddd2 --- /dev/null +++ b/tests/baselines/reference/interfaceDoesNotDependOnBaseTypes.js @@ -0,0 +1,16 @@ +//// [interfaceDoesNotDependOnBaseTypes.ts] +var x: StringTree; +if (typeof x !== "string") { + x.push(""); + x.push([""]); +} + +type StringTree = string | StringTreeArray; +interface StringTreeArray extends Array { } + +//// [interfaceDoesNotDependOnBaseTypes.js] +var x; +if (typeof x !== "string") { + x.push(""); + x.push([""]); +} diff --git a/tests/baselines/reference/interfaceDoesNotDependOnBaseTypes.types b/tests/baselines/reference/interfaceDoesNotDependOnBaseTypes.types new file mode 100644 index 0000000000..7b5d60a526 --- /dev/null +++ b/tests/baselines/reference/interfaceDoesNotDependOnBaseTypes.types @@ -0,0 +1,36 @@ +=== tests/cases/conformance/types/typeAliases/interfaceDoesNotDependOnBaseTypes.ts === +var x: StringTree; +>x : string | StringTreeArray +>StringTree : string | StringTreeArray + +if (typeof x !== "string") { +>typeof x !== "string" : boolean +>typeof x : string +>x : string | StringTreeArray +>"string" : string + + x.push(""); +>x.push("") : number +>x.push : (...items: (string | StringTreeArray)[]) => number +>x : StringTreeArray +>push : (...items: (string | StringTreeArray)[]) => number +>"" : string + + x.push([""]); +>x.push([""]) : number +>x.push : (...items: (string | StringTreeArray)[]) => number +>x : StringTreeArray +>push : (...items: (string | StringTreeArray)[]) => number +>[""] : string[] +>"" : string +} + +type StringTree = string | StringTreeArray; +>StringTree : string | StringTreeArray +>StringTreeArray : StringTreeArray + +interface StringTreeArray extends Array { } +>StringTreeArray : StringTreeArray +>Array : T[] +>StringTree : string | StringTreeArray + diff --git a/tests/cases/conformance/types/typeAliases/classDoesNotDependOnBaseTypes.ts b/tests/cases/conformance/types/typeAliases/classDoesNotDependOnBaseTypes.ts new file mode 100644 index 0000000000..98f94718f3 --- /dev/null +++ b/tests/cases/conformance/types/typeAliases/classDoesNotDependOnBaseTypes.ts @@ -0,0 +1,12 @@ +var x: StringTree; +if (typeof x !== "string") { + x[0] = ""; + x[0] = new StringTreeCollection; +} + +type StringTree = string | StringTreeCollection; +class StringTreeCollectionBase { + [n: number]: StringTree; +} + +class StringTreeCollection extends StringTreeCollectionBase { } \ No newline at end of file diff --git a/tests/cases/conformance/types/typeAliases/interfaceDoesNotDependOnBaseTypes.ts b/tests/cases/conformance/types/typeAliases/interfaceDoesNotDependOnBaseTypes.ts new file mode 100644 index 0000000000..4e380e96b3 --- /dev/null +++ b/tests/cases/conformance/types/typeAliases/interfaceDoesNotDependOnBaseTypes.ts @@ -0,0 +1,8 @@ +var x: StringTree; +if (typeof x !== "string") { + x.push(""); + x.push([""]); +} + +type StringTree = string | StringTreeArray; +interface StringTreeArray extends Array { } \ No newline at end of file From 2c4404b9c463a0fee1007862b7538a32e076e516 Mon Sep 17 00:00:00 2001 From: Jason Freeman Date: Thu, 16 Apr 2015 18:24:10 -0700 Subject: [PATCH 3/5] Clean up some repeated calls to getBaseTypes --- src/compiler/checker.ts | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f9a6d515d1..b36d646671 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2670,9 +2670,10 @@ module ts { let constructSignatures = type.declaredConstructSignatures; let stringIndexType = type.declaredStringIndexType; let numberIndexType = type.declaredNumberIndexType; - if (getBaseTypes(type).length) { + let baseTypes = getBaseTypes(type); + if (baseTypes.length) { members = createSymbolTable(type.declaredProperties); - forEach(getBaseTypes(type), baseType => { + forEach(baseTypes, baseType => { addInheritedMembers(members, getPropertiesOfObjectType(baseType)); callSignatures = concatenate(callSignatures, getSignaturesOfType(baseType, SignatureKind.Call)); constructSignatures = concatenate(constructSignatures, getSignaturesOfType(baseType, SignatureKind.Construct)); @@ -2720,9 +2721,10 @@ module ts { sig.minArgumentCount, sig.hasRestParameter, sig.hasStringLiterals); } - function getDefaultConstructSignatures(classType: InterfaceType): Signature[] { - if (getBaseTypes(classType).length) { - let baseType = getBaseTypes(classType)[0]; + function getDefaultConstructSignatures(classType: InterfaceType): Signature[]{ + let baseTypes = getBaseTypes(classType); + if (baseTypes.length) { + let baseType = baseTypes[0]; let baseSignatures = getSignaturesOfType(getTypeOfSymbol(baseType.symbol), SignatureKind.Construct); return map(baseSignatures, baseSignature => { let signature = baseType.flags & TypeFlags.Reference ? @@ -2844,9 +2846,10 @@ module ts { if (!constructSignatures.length) { constructSignatures = getDefaultConstructSignatures(classType); } - if (getBaseTypes(classType).length) { + let baseTypes = getBaseTypes(classType); + if (baseTypes.length) { members = createSymbolTable(getNamedMembers(members)); - addInheritedMembers(members, getPropertiesOfObjectType(getTypeOfSymbol(getBaseTypes(classType)[0].symbol))); + addInheritedMembers(members, getPropertiesOfObjectType(getTypeOfSymbol(baseTypes[0].symbol))); } } stringIndexType = undefined; @@ -5574,7 +5577,8 @@ module ts { let baseClass: Type; if (enclosingClass && getClassExtendsHeritageClauseElement(enclosingClass)) { let classType = getDeclaredTypeOfSymbol(getSymbolOfNode(enclosingClass)); - baseClass = getBaseTypes(classType).length && getBaseTypes(classType)[0]; + let baseTypes = getBaseTypes(classType); + baseClass = baseTypes.length && baseTypes[0]; } if (!baseClass) { @@ -9977,9 +9981,10 @@ module ts { emitExtends = emitExtends || !isInAmbientContext(node); checkHeritageClauseElement(baseTypeNode); } - if (getBaseTypes(type).length) { + let baseTypes = getBaseTypes(type); + if (baseTypes.length) { if (produceDiagnostics) { - let baseType = getBaseTypes(type)[0]; + let baseType = baseTypes[0]; checkTypeAssignableTo(type, baseType, node.name || node, Diagnostics.Class_0_incorrectly_extends_base_class_1); let staticBaseType = getTypeOfSymbol(baseType.symbol); checkTypeAssignableTo(staticType, getTypeWithoutConstructors(staticBaseType), node.name || node, @@ -9993,7 +9998,7 @@ module ts { } } - if (getBaseTypes(type).length || (baseTypeNode && compilerOptions.separateCompilation)) { + if (baseTypes.length || (baseTypeNode && compilerOptions.separateCompilation)) { // Check that base type can be evaluated as expression checkExpressionOrQualifiedName(baseTypeNode.expression); } @@ -10137,7 +10142,8 @@ module ts { } function checkInheritedPropertiesAreIdentical(type: InterfaceType, typeNode: Node): boolean { - if (!getBaseTypes(type).length || getBaseTypes(type).length === 1) { + let baseTypes = getBaseTypes(type); + if (!baseTypes.length || baseTypes.length === 1) { return true; } @@ -10145,7 +10151,7 @@ module ts { forEach(type.declaredProperties, p => { seen[p.name] = { prop: p, containingType: type }; }); let ok = true; - for (let base of getBaseTypes(type)) { + for (let base of baseTypes) { let properties = getPropertiesOfObjectType(base); for (let prop of properties) { if (!hasProperty(seen, prop.name)) { From 28a0f941c87eb01e9b76e7151bdf4588cb55cc05 Mon Sep 17 00:00:00 2001 From: Jason Freeman Date: Fri, 17 Apr 2015 12:57:29 -0700 Subject: [PATCH 4/5] Accept symbol baselines --- .../classDoesNotDependOnBaseTypes.symbols | 32 +++++++++++++++++++ .../interfaceDoesNotDependOnBaseTypes.symbols | 28 ++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 tests/baselines/reference/classDoesNotDependOnBaseTypes.symbols create mode 100644 tests/baselines/reference/interfaceDoesNotDependOnBaseTypes.symbols diff --git a/tests/baselines/reference/classDoesNotDependOnBaseTypes.symbols b/tests/baselines/reference/classDoesNotDependOnBaseTypes.symbols new file mode 100644 index 0000000000..e45b00f1e8 --- /dev/null +++ b/tests/baselines/reference/classDoesNotDependOnBaseTypes.symbols @@ -0,0 +1,32 @@ +=== tests/cases/conformance/types/typeAliases/classDoesNotDependOnBaseTypes.ts === +var x: StringTree; +>x : Symbol(x, Decl(classDoesNotDependOnBaseTypes.ts, 0, 3)) +>StringTree : Symbol(StringTree, Decl(classDoesNotDependOnBaseTypes.ts, 4, 1)) + +if (typeof x !== "string") { +>x : Symbol(x, Decl(classDoesNotDependOnBaseTypes.ts, 0, 3)) + + x[0] = ""; +>x : Symbol(x, Decl(classDoesNotDependOnBaseTypes.ts, 0, 3)) + + x[0] = new StringTreeCollection; +>x : Symbol(x, Decl(classDoesNotDependOnBaseTypes.ts, 0, 3)) +>StringTreeCollection : Symbol(StringTreeCollection, Decl(classDoesNotDependOnBaseTypes.ts, 9, 1)) +} + +type StringTree = string | StringTreeCollection; +>StringTree : Symbol(StringTree, Decl(classDoesNotDependOnBaseTypes.ts, 4, 1)) +>StringTreeCollection : Symbol(StringTreeCollection, Decl(classDoesNotDependOnBaseTypes.ts, 9, 1)) + +class StringTreeCollectionBase { +>StringTreeCollectionBase : Symbol(StringTreeCollectionBase, Decl(classDoesNotDependOnBaseTypes.ts, 6, 48)) + + [n: number]: StringTree; +>n : Symbol(n, Decl(classDoesNotDependOnBaseTypes.ts, 8, 5)) +>StringTree : Symbol(StringTree, Decl(classDoesNotDependOnBaseTypes.ts, 4, 1)) +} + +class StringTreeCollection extends StringTreeCollectionBase { } +>StringTreeCollection : Symbol(StringTreeCollection, Decl(classDoesNotDependOnBaseTypes.ts, 9, 1)) +>StringTreeCollectionBase : Symbol(StringTreeCollectionBase, Decl(classDoesNotDependOnBaseTypes.ts, 6, 48)) + diff --git a/tests/baselines/reference/interfaceDoesNotDependOnBaseTypes.symbols b/tests/baselines/reference/interfaceDoesNotDependOnBaseTypes.symbols new file mode 100644 index 0000000000..6b45cd1daa --- /dev/null +++ b/tests/baselines/reference/interfaceDoesNotDependOnBaseTypes.symbols @@ -0,0 +1,28 @@ +=== tests/cases/conformance/types/typeAliases/interfaceDoesNotDependOnBaseTypes.ts === +var x: StringTree; +>x : Symbol(x, Decl(interfaceDoesNotDependOnBaseTypes.ts, 0, 3)) +>StringTree : Symbol(StringTree, Decl(interfaceDoesNotDependOnBaseTypes.ts, 4, 1)) + +if (typeof x !== "string") { +>x : Symbol(x, Decl(interfaceDoesNotDependOnBaseTypes.ts, 0, 3)) + + x.push(""); +>x.push : Symbol(Array.push, Decl(lib.d.ts, 1016, 29)) +>x : Symbol(x, Decl(interfaceDoesNotDependOnBaseTypes.ts, 0, 3)) +>push : Symbol(Array.push, Decl(lib.d.ts, 1016, 29)) + + x.push([""]); +>x.push : Symbol(Array.push, Decl(lib.d.ts, 1016, 29)) +>x : Symbol(x, Decl(interfaceDoesNotDependOnBaseTypes.ts, 0, 3)) +>push : Symbol(Array.push, Decl(lib.d.ts, 1016, 29)) +} + +type StringTree = string | StringTreeArray; +>StringTree : Symbol(StringTree, Decl(interfaceDoesNotDependOnBaseTypes.ts, 4, 1)) +>StringTreeArray : Symbol(StringTreeArray, Decl(interfaceDoesNotDependOnBaseTypes.ts, 6, 43)) + +interface StringTreeArray extends Array { } +>StringTreeArray : Symbol(StringTreeArray, Decl(interfaceDoesNotDependOnBaseTypes.ts, 6, 43)) +>Array : Symbol(Array, Decl(lib.d.ts, 1000, 23), Decl(lib.d.ts, 1171, 11)) +>StringTree : Symbol(StringTree, Decl(interfaceDoesNotDependOnBaseTypes.ts, 4, 1)) + From 47ccf77cea790b2028ccc0c50c70f1a12c4f3c3f Mon Sep 17 00:00:00 2001 From: Jason Freeman Date: Fri, 17 Apr 2015 14:25:06 -0700 Subject: [PATCH 5/5] Address PR feedback --- src/compiler/checker.ts | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4adf483e23..70490252ab 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2452,20 +2452,21 @@ module ts { return result; } - function getBaseTypes(type: InterfaceType): ObjectType[] { - if (!(type).baseTypes) { + function getBaseTypes(type: InterfaceType): ObjectType[]{ + let typeWithBaseTypes = type; + if (!typeWithBaseTypes.baseTypes) { if (type.symbol.flags & SymbolFlags.Class) { - resolveBaseTypesOfClass(type); + resolveBaseTypesOfClass(typeWithBaseTypes); } else if (type.symbol.flags & SymbolFlags.Interface) { - resolveBaseTypesOfInterface(type); + resolveBaseTypesOfInterface(typeWithBaseTypes); } else { Debug.fail("type must be class or interface"); } } - return (type).baseTypes; + return typeWithBaseTypes.baseTypes; } function resolveBaseTypesOfClass(type: InterfaceTypeWithBaseTypes): void { @@ -2492,9 +2493,9 @@ module ts { function resolveBaseTypesOfInterface(type: InterfaceTypeWithBaseTypes): void { type.baseTypes = []; - forEach(type.symbol.declarations, declaration => { + for (let declaration of type.symbol.declarations) { if (declaration.kind === SyntaxKind.InterfaceDeclaration && getInterfaceBaseTypeNodes(declaration)) { - forEach(getInterfaceBaseTypeNodes(declaration), node => { + for (let node of getInterfaceBaseTypeNodes(declaration)) { let baseType = getTypeFromHeritageClauseElement(node); if (baseType !== unknownType) { @@ -2510,9 +2511,9 @@ module ts { error(node, Diagnostics.An_interface_may_only_extend_a_class_or_another_interface); } } - }); + } } - }); + } } function getDeclaredTypeOfClass(symbol: Symbol): InterfaceType { @@ -2674,13 +2675,13 @@ module ts { let baseTypes = getBaseTypes(type); if (baseTypes.length) { members = createSymbolTable(type.declaredProperties); - forEach(baseTypes, baseType => { + for (let baseType of baseTypes) { addInheritedMembers(members, getPropertiesOfObjectType(baseType)); callSignatures = concatenate(callSignatures, getSignaturesOfType(baseType, SignatureKind.Call)); constructSignatures = concatenate(constructSignatures, getSignaturesOfType(baseType, SignatureKind.Construct)); stringIndexType = stringIndexType || getIndexTypeOfType(baseType, IndexKind.String); numberIndexType = numberIndexType || getIndexTypeOfType(baseType, IndexKind.Number); - }); + } } setObjectTypeMembers(type, members, callSignatures, constructSignatures, stringIndexType, numberIndexType); } @@ -10148,7 +10149,7 @@ module ts { function checkInheritedPropertiesAreIdentical(type: InterfaceType, typeNode: Node): boolean { let baseTypes = getBaseTypes(type); - if (!baseTypes.length || baseTypes.length === 1) { + if (baseTypes.length < 2) { return true; }