Merge pull request #2813 from Microsoft/lazyBaseTypes

Lazy base types
This commit is contained in:
Jason Freeman 2015-04-17 14:31:17 -07:00
commit f3c073e72d
10 changed files with 307 additions and 60 deletions

View file

@ -2424,7 +2424,7 @@ module ts {
return check(type);
function check(type: InterfaceType): boolean {
let target = <InterfaceType>getTargetType(type);
return target === checkBase || forEach(target.baseTypes, check);
return target === checkBase || forEach(getBaseTypes(target), check);
}
}
@ -2452,6 +2452,70 @@ module ts {
return result;
}
function getBaseTypes(type: InterfaceType): ObjectType[]{
let typeWithBaseTypes = <InterfaceTypeWithBaseTypes>type;
if (!typeWithBaseTypes.baseTypes) {
if (type.symbol.flags & SymbolFlags.Class) {
resolveBaseTypesOfClass(typeWithBaseTypes);
}
else if (type.symbol.flags & SymbolFlags.Interface) {
resolveBaseTypesOfInterface(typeWithBaseTypes);
}
else {
Debug.fail("type must be class or interface");
}
}
return typeWithBaseTypes.baseTypes;
}
function resolveBaseTypesOfClass(type: InterfaceTypeWithBaseTypes): void {
type.baseTypes = [];
let declaration = <ClassDeclaration>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(<InterfaceType>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 = [];
for (let declaration of type.symbol.declarations) {
if (declaration.kind === SyntaxKind.InterfaceDeclaration && getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration)) {
for (let node of getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration)) {
let baseType = getTypeFromHeritageClauseElement(node);
if (baseType !== unknownType) {
if (getTargetType(baseType).flags & (TypeFlags.Class | TypeFlags.Interface)) {
if (type !== baseType && !hasBaseType(<InterfaceType>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) {
@ -2465,25 +2529,7 @@ module ts {
(<GenericType>type).target = <GenericType>type;
(<GenericType>type).typeArguments = type.typeParameters;
}
type.baseTypes = [];
let declaration = <ClassDeclaration>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(<InterfaceType>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;
@ -2506,28 +2552,7 @@ module ts {
(<GenericType>type).target = <GenericType>type;
(<GenericType>type).typeArguments = type.typeParameters;
}
type.baseTypes = [];
forEach(symbol.declarations, declaration => {
if (declaration.kind === SyntaxKind.InterfaceDeclaration && getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration)) {
forEach(getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration), node => {
let baseType = getTypeFromHeritageClauseElement(node);
if (baseType !== unknownType) {
if (getTargetType(baseType).flags & (TypeFlags.Class | TypeFlags.Interface)) {
if (type !== baseType && !hasBaseType(<InterfaceType>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"]);
@ -2647,15 +2672,16 @@ module ts {
let constructSignatures = type.declaredConstructSignatures;
let stringIndexType = type.declaredStringIndexType;
let numberIndexType = type.declaredNumberIndexType;
if (type.baseTypes.length) {
let baseTypes = getBaseTypes(type);
if (baseTypes.length) {
members = createSymbolTable(type.declaredProperties);
forEach(type.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);
}
@ -2668,7 +2694,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,9 +2723,10 @@ module ts {
sig.minArgumentCount, sig.hasRestParameter, sig.hasStringLiterals);
}
function getDefaultConstructSignatures(classType: InterfaceType): Signature[] {
if (classType.baseTypes.length) {
let baseType = classType.baseTypes[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 ?
@ -2821,9 +2848,10 @@ module ts {
if (!constructSignatures.length) {
constructSignatures = getDefaultConstructSignatures(classType);
}
if (classType.baseTypes.length) {
let baseTypes = getBaseTypes(classType);
if (baseTypes.length) {
members = createSymbolTable(getNamedMembers(members));
addInheritedMembers(members, getPropertiesOfObjectType(getTypeOfSymbol(classType.baseTypes[0].symbol)));
addInheritedMembers(members, getPropertiesOfObjectType(getTypeOfSymbol(baseTypes[0].symbol)));
}
}
stringIndexType = undefined;
@ -5551,7 +5579,8 @@ module ts {
let baseClass: Type;
if (enclosingClass && getClassExtendsHeritageClauseElement(enclosingClass)) {
let classType = <InterfaceType>getDeclaredTypeOfSymbol(getSymbolOfNode(enclosingClass));
baseClass = classType.baseTypes.length && classType.baseTypes[0];
let baseTypes = getBaseTypes(classType);
baseClass = baseTypes.length && baseTypes[0];
}
if (!baseClass) {
@ -9834,7 +9863,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((<InterfaceType>type).baseTypes, base => getIndexTypeOfType(base, IndexKind.String) && getIndexTypeOfType(base, IndexKind.Number));
let someBaseTypeHasBothIndexers = forEach(getBaseTypes(<InterfaceType>type), base => getIndexTypeOfType(base, IndexKind.String) && getIndexTypeOfType(base, IndexKind.Number));
errorNode = someBaseTypeHasBothIndexers ? undefined : type.symbol.declarations[0];
}
}
@ -9874,7 +9903,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((<InterfaceType>containingType).baseTypes, base => getPropertyOfObjectType(base, prop.name) && getIndexTypeOfType(base, indexKind));
let someBaseClassHasBothPropertyAndIndexer = forEach(getBaseTypes(<InterfaceType>containingType), base => getPropertyOfObjectType(base, prop.name) && getIndexTypeOfType(base, indexKind));
errorNode = someBaseClassHasBothPropertyAndIndexer ? undefined : containingType.symbol.declarations[0];
}
@ -9958,9 +9987,10 @@ module ts {
emitExtends = emitExtends || !isInAmbientContext(node);
checkHeritageClauseElement(baseTypeNode);
}
if (type.baseTypes.length) {
let baseTypes = getBaseTypes(type);
if (baseTypes.length) {
if (produceDiagnostics) {
let baseType = type.baseTypes[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,
@ -9974,7 +10004,7 @@ module ts {
}
}
if (type.baseTypes.length || (baseTypeNode && compilerOptions.separateCompilation)) {
if (baseTypes.length || (baseTypeNode && compilerOptions.separateCompilation)) {
// Check that base type can be evaluated as expression
checkExpressionOrQualifiedName(baseTypeNode.expression);
}
@ -10118,7 +10148,8 @@ module ts {
}
function checkInheritedPropertiesAreIdentical(type: InterfaceType, typeNode: Node): boolean {
if (!type.baseTypes.length || type.baseTypes.length === 1) {
let baseTypes = getBaseTypes(type);
if (baseTypes.length < 2) {
return true;
}
@ -10126,7 +10157,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 baseTypes) {
let properties = getPropertiesOfObjectType(base);
for (let prop of properties) {
if (!hasProperty(seen, prop.name)) {
@ -10174,7 +10205,7 @@ module ts {
let type = <InterfaceType>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);

View file

@ -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

View file

@ -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);

View file

@ -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))

View file

@ -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

View file

@ -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<StringTree> { }
//// [interfaceDoesNotDependOnBaseTypes.js]
var x;
if (typeof x !== "string") {
x.push("");
x.push([""]);
}

View file

@ -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<StringTree> { }
>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))

View file

@ -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<StringTree> { }
>StringTreeArray : StringTreeArray
>Array : T[]
>StringTree : string | StringTreeArray

View file

@ -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 { }

View file

@ -0,0 +1,8 @@
var x: StringTree;
if (typeof x !== "string") {
x.push("");
x.push([""]);
}
type StringTree = string | StringTreeArray;
interface StringTreeArray extends Array<StringTree> { }