Skip to content

Commit 3b1c833

Browse files
committed
Lazily compute base types
1 parent 905375d commit 3b1c833

File tree

2 files changed

+85
-58
lines changed

2 files changed

+85
-58
lines changed

src/compiler/checker.ts

Lines changed: 81 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2423,7 +2423,7 @@ module ts {
24232423
return check(type);
24242424
function check(type: InterfaceType): boolean {
24252425
let target = <InterfaceType>getTargetType(type);
2426-
return target === checkBase || forEach(target.baseTypes, check);
2426+
return target === checkBase || forEach(getBaseTypes(target), check);
24272427
}
24282428
}
24292429

@@ -2451,6 +2451,69 @@ module ts {
24512451
return result;
24522452
}
24532453

2454+
function getBaseTypes(type: InterfaceType): ObjectType[] {
2455+
if (!(<InterfaceTypeWithBaseTypes>type).baseTypes) {
2456+
if (type.symbol.flags & SymbolFlags.Class) {
2457+
resolveBaseTypesOfClass(<InterfaceTypeWithBaseTypes>type);
2458+
}
2459+
else if (type.symbol.flags & SymbolFlags.Interface) {
2460+
resolveBaseTypesOfInterface(<InterfaceTypeWithBaseTypes>type);
2461+
}
2462+
else {
2463+
Debug.fail("type must be class or interface");
2464+
}
2465+
}
2466+
2467+
return (<InterfaceTypeWithBaseTypes>type).baseTypes;
2468+
}
2469+
2470+
function resolveBaseTypesOfClass(type: InterfaceTypeWithBaseTypes): void {
2471+
type.baseTypes = [];
2472+
let declaration = <ClassDeclaration>getDeclarationOfKind(type.symbol, SyntaxKind.ClassDeclaration);
2473+
let baseTypeNode = getClassExtendsHeritageClauseElement(declaration);
2474+
if (baseTypeNode) {
2475+
let baseType = getTypeFromHeritageClauseElement(baseTypeNode);
2476+
if (baseType !== unknownType) {
2477+
if (getTargetType(baseType).flags & TypeFlags.Class) {
2478+
if (type !== baseType && !hasBaseType(<InterfaceType>baseType, type)) {
2479+
type.baseTypes.push(baseType);
2480+
}
2481+
else {
2482+
error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType));
2483+
}
2484+
}
2485+
else {
2486+
error(baseTypeNode, Diagnostics.A_class_may_only_extend_another_class);
2487+
}
2488+
}
2489+
}
2490+
}
2491+
2492+
function resolveBaseTypesOfInterface(type: InterfaceTypeWithBaseTypes): void {
2493+
type.baseTypes = [];
2494+
forEach(type.symbol.declarations, declaration => {
2495+
if (declaration.kind === SyntaxKind.InterfaceDeclaration && getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration)) {
2496+
forEach(getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration), node => {
2497+
let baseType = getTypeFromHeritageClauseElement(node);
2498+
2499+
if (baseType !== unknownType) {
2500+
if (getTargetType(baseType).flags & (TypeFlags.Class | TypeFlags.Interface)) {
2501+
if (type !== baseType && !hasBaseType(<InterfaceType>baseType, type)) {
2502+
type.baseTypes.push(baseType);
2503+
}
2504+
else {
2505+
error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType));
2506+
}
2507+
}
2508+
else {
2509+
error(node, Diagnostics.An_interface_may_only_extend_a_class_or_another_interface);
2510+
}
2511+
}
2512+
});
2513+
}
2514+
});
2515+
}
2516+
24542517
function getDeclaredTypeOfClass(symbol: Symbol): InterfaceType {
24552518
let links = getSymbolLinks(symbol);
24562519
if (!links.declaredType) {
@@ -2464,25 +2527,7 @@ module ts {
24642527
(<GenericType>type).target = <GenericType>type;
24652528
(<GenericType>type).typeArguments = type.typeParameters;
24662529
}
2467-
type.baseTypes = [];
2468-
let declaration = <ClassDeclaration>getDeclarationOfKind(symbol, SyntaxKind.ClassDeclaration);
2469-
let baseTypeNode = getClassExtendsHeritageClauseElement(declaration);
2470-
if (baseTypeNode) {
2471-
let baseType = getTypeFromHeritageClauseElement(baseTypeNode);
2472-
if (baseType !== unknownType) {
2473-
if (getTargetType(baseType).flags & TypeFlags.Class) {
2474-
if (type !== baseType && !hasBaseType(<InterfaceType>baseType, type)) {
2475-
type.baseTypes.push(baseType);
2476-
}
2477-
else {
2478-
error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType));
2479-
}
2480-
}
2481-
else {
2482-
error(baseTypeNode, Diagnostics.A_class_may_only_extend_another_class);
2483-
}
2484-
}
2485-
}
2530+
24862531
type.declaredProperties = getNamedMembers(symbol.members);
24872532
type.declaredCallSignatures = emptyArray;
24882533
type.declaredConstructSignatures = emptyArray;
@@ -2505,28 +2550,7 @@ module ts {
25052550
(<GenericType>type).target = <GenericType>type;
25062551
(<GenericType>type).typeArguments = type.typeParameters;
25072552
}
2508-
type.baseTypes = [];
2509-
forEach(symbol.declarations, declaration => {
2510-
if (declaration.kind === SyntaxKind.InterfaceDeclaration && getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration)) {
2511-
forEach(getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration), node => {
2512-
let baseType = getTypeFromHeritageClauseElement(node);
25132553

2514-
if (baseType !== unknownType) {
2515-
if (getTargetType(baseType).flags & (TypeFlags.Class | TypeFlags.Interface)) {
2516-
if (type !== baseType && !hasBaseType(<InterfaceType>baseType, type)) {
2517-
type.baseTypes.push(baseType);
2518-
}
2519-
else {
2520-
error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType));
2521-
}
2522-
}
2523-
else {
2524-
error(node, Diagnostics.An_interface_may_only_extend_a_class_or_another_interface);
2525-
}
2526-
}
2527-
});
2528-
}
2529-
});
25302554
type.declaredProperties = getNamedMembers(symbol.members);
25312555
type.declaredCallSignatures = getSignaturesOfSymbol(symbol.members["__call"]);
25322556
type.declaredConstructSignatures = getSignaturesOfSymbol(symbol.members["__new"]);
@@ -2646,9 +2670,9 @@ module ts {
26462670
let constructSignatures = type.declaredConstructSignatures;
26472671
let stringIndexType = type.declaredStringIndexType;
26482672
let numberIndexType = type.declaredNumberIndexType;
2649-
if (type.baseTypes.length) {
2673+
if (getBaseTypes(type).length) {
26502674
members = createSymbolTable(type.declaredProperties);
2651-
forEach(type.baseTypes, baseType => {
2675+
forEach(getBaseTypes(type), baseType => {
26522676
addInheritedMembers(members, getPropertiesOfObjectType(baseType));
26532677
callSignatures = concatenate(callSignatures, getSignaturesOfType(baseType, SignatureKind.Call));
26542678
constructSignatures = concatenate(constructSignatures, getSignaturesOfType(baseType, SignatureKind.Construct));
@@ -2667,7 +2691,7 @@ module ts {
26672691
let constructSignatures = instantiateList(target.declaredConstructSignatures, mapper, instantiateSignature);
26682692
let stringIndexType = target.declaredStringIndexType ? instantiateType(target.declaredStringIndexType, mapper) : undefined;
26692693
let numberIndexType = target.declaredNumberIndexType ? instantiateType(target.declaredNumberIndexType, mapper) : undefined;
2670-
forEach(target.baseTypes, baseType => {
2694+
forEach(getBaseTypes(target), baseType => {
26712695
let instantiatedBaseType = instantiateType(baseType, mapper);
26722696
addInheritedMembers(members, getPropertiesOfObjectType(instantiatedBaseType));
26732697
callSignatures = concatenate(callSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Call));
@@ -2697,8 +2721,8 @@ module ts {
26972721
}
26982722

26992723
function getDefaultConstructSignatures(classType: InterfaceType): Signature[] {
2700-
if (classType.baseTypes.length) {
2701-
let baseType = classType.baseTypes[0];
2724+
if (getBaseTypes(classType).length) {
2725+
let baseType = getBaseTypes(classType)[0];
27022726
let baseSignatures = getSignaturesOfType(getTypeOfSymbol(baseType.symbol), SignatureKind.Construct);
27032727
return map(baseSignatures, baseSignature => {
27042728
let signature = baseType.flags & TypeFlags.Reference ?
@@ -2820,9 +2844,9 @@ module ts {
28202844
if (!constructSignatures.length) {
28212845
constructSignatures = getDefaultConstructSignatures(classType);
28222846
}
2823-
if (classType.baseTypes.length) {
2847+
if (getBaseTypes(classType).length) {
28242848
members = createSymbolTable(getNamedMembers(members));
2825-
addInheritedMembers(members, getPropertiesOfObjectType(getTypeOfSymbol(classType.baseTypes[0].symbol)));
2849+
addInheritedMembers(members, getPropertiesOfObjectType(getTypeOfSymbol(getBaseTypes(classType)[0].symbol)));
28262850
}
28272851
}
28282852
stringIndexType = undefined;
@@ -5550,7 +5574,7 @@ module ts {
55505574
let baseClass: Type;
55515575
if (enclosingClass && getClassExtendsHeritageClauseElement(enclosingClass)) {
55525576
let classType = <InterfaceType>getDeclaredTypeOfSymbol(getSymbolOfNode(enclosingClass));
5553-
baseClass = classType.baseTypes.length && classType.baseTypes[0];
5577+
baseClass = getBaseTypes(classType).length && getBaseTypes(classType)[0];
55545578
}
55555579

55565580
if (!baseClass) {
@@ -9829,7 +9853,7 @@ module ts {
98299853
errorNode = declaredNumberIndexer || declaredStringIndexer;
98309854
// condition 'errorNode === undefined' may appear if types does not declare nor string neither number indexer
98319855
if (!errorNode && (type.flags & TypeFlags.Interface)) {
9832-
let someBaseTypeHasBothIndexers = forEach((<InterfaceType>type).baseTypes, base => getIndexTypeOfType(base, IndexKind.String) && getIndexTypeOfType(base, IndexKind.Number));
9856+
let someBaseTypeHasBothIndexers = forEach(getBaseTypes(<InterfaceType>type), base => getIndexTypeOfType(base, IndexKind.String) && getIndexTypeOfType(base, IndexKind.Number));
98339857
errorNode = someBaseTypeHasBothIndexers ? undefined : type.symbol.declarations[0];
98349858
}
98359859
}
@@ -9869,7 +9893,7 @@ module ts {
98699893
// for interfaces property and indexer might be inherited from different bases
98709894
// check if any base class already has both property and indexer.
98719895
// check should be performed only if 'type' is the first type that brings property\indexer together
9872-
let someBaseClassHasBothPropertyAndIndexer = forEach((<InterfaceType>containingType).baseTypes, base => getPropertyOfObjectType(base, prop.name) && getIndexTypeOfType(base, indexKind));
9896+
let someBaseClassHasBothPropertyAndIndexer = forEach(getBaseTypes(<InterfaceType>containingType), base => getPropertyOfObjectType(base, prop.name) && getIndexTypeOfType(base, indexKind));
98739897
errorNode = someBaseClassHasBothPropertyAndIndexer ? undefined : containingType.symbol.declarations[0];
98749898
}
98759899

@@ -9953,9 +9977,9 @@ module ts {
99539977
emitExtends = emitExtends || !isInAmbientContext(node);
99549978
checkHeritageClauseElement(baseTypeNode);
99559979
}
9956-
if (type.baseTypes.length) {
9980+
if (getBaseTypes(type).length) {
99579981
if (produceDiagnostics) {
9958-
let baseType = type.baseTypes[0];
9982+
let baseType = getBaseTypes(type)[0];
99599983
checkTypeAssignableTo(type, baseType, node.name || node, Diagnostics.Class_0_incorrectly_extends_base_class_1);
99609984
let staticBaseType = getTypeOfSymbol(baseType.symbol);
99619985
checkTypeAssignableTo(staticType, getTypeWithoutConstructors(staticBaseType), node.name || node,
@@ -9969,7 +9993,7 @@ module ts {
99699993
}
99709994
}
99719995

9972-
if (type.baseTypes.length || (baseTypeNode && compilerOptions.separateCompilation)) {
9996+
if (getBaseTypes(type).length || (baseTypeNode && compilerOptions.separateCompilation)) {
99739997
// Check that base type can be evaluated as expression
99749998
checkExpressionOrQualifiedName(baseTypeNode.expression);
99759999
}
@@ -10113,15 +10137,15 @@ module ts {
1011310137
}
1011410138

1011510139
function checkInheritedPropertiesAreIdentical(type: InterfaceType, typeNode: Node): boolean {
10116-
if (!type.baseTypes.length || type.baseTypes.length === 1) {
10140+
if (!getBaseTypes(type).length || getBaseTypes(type).length === 1) {
1011710141
return true;
1011810142
}
1011910143

1012010144
let seen: Map<{ prop: Symbol; containingType: Type }> = {};
1012110145
forEach(type.declaredProperties, p => { seen[p.name] = { prop: p, containingType: type }; });
1012210146
let ok = true;
1012310147

10124-
for (let base of type.baseTypes) {
10148+
for (let base of getBaseTypes(type)) {
1012510149
let properties = getPropertiesOfObjectType(base);
1012610150
for (let prop of properties) {
1012710151
if (!hasProperty(seen, prop.name)) {
@@ -10169,7 +10193,7 @@ module ts {
1016910193
let type = <InterfaceType>getDeclaredTypeOfSymbol(symbol);
1017010194
// run subsequent checks only if first set succeeded
1017110195
if (checkInheritedPropertiesAreIdentical(type, node.name)) {
10172-
forEach(type.baseTypes, baseType => {
10196+
forEach(getBaseTypes(type), baseType => {
1017310197
checkTypeAssignableTo(type, baseType, node.name, Diagnostics.Interface_0_incorrectly_extends_interface_1);
1017410198
});
1017510199
checkIndexConstraints(type);

src/compiler/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1485,14 +1485,17 @@ module ts {
14851485
// Class and interface types (TypeFlags.Class and TypeFlags.Interface)
14861486
export interface InterfaceType extends ObjectType {
14871487
typeParameters: TypeParameter[]; // Type parameters (undefined if non-generic)
1488-
baseTypes: ObjectType[]; // Base types
14891488
declaredProperties: Symbol[]; // Declared members
14901489
declaredCallSignatures: Signature[]; // Declared call signatures
14911490
declaredConstructSignatures: Signature[]; // Declared construct signatures
14921491
declaredStringIndexType: Type; // Declared string index type
14931492
declaredNumberIndexType: Type; // Declared numeric index type
14941493
}
14951494

1495+
export interface InterfaceTypeWithBaseTypes extends InterfaceType {
1496+
baseTypes: ObjectType[];
1497+
}
1498+
14961499
// Type references (TypeFlags.Reference)
14971500
export interface TypeReference extends ObjectType {
14981501
target: GenericType; // Type reference target

0 commit comments

Comments
 (0)