Skip to content

Commit 614afb7

Browse files
committed
Merge pull request microsoft#7448 from Microsoft/optimizeInstantiation
Skip unnecessary instatiation of anonymous types
2 parents ac147b1 + 4f441bd commit 614afb7

9 files changed

Lines changed: 2428 additions & 26 deletions

src/compiler/checker.ts

Lines changed: 73 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5045,42 +5045,29 @@ namespace ts {
50455045
return t => t === source1 ? target1 : t === source2 ? target2 : t;
50465046
}
50475047

5048-
function createTypeMapper(sources: Type[], targets: Type[]): TypeMapper {
5049-
switch (sources.length) {
5050-
case 1: return createUnaryTypeMapper(sources[0], targets[0]);
5051-
case 2: return createBinaryTypeMapper(sources[0], targets[0], sources[1], targets[1]);
5052-
}
5048+
function createArrayTypeMapper(sources: Type[], targets: Type[]): TypeMapper {
50535049
return t => {
50545050
for (let i = 0; i < sources.length; i++) {
50555051
if (t === sources[i]) {
5056-
return targets[i];
5052+
return targets ? targets[i] : anyType;
50575053
}
50585054
}
50595055
return t;
50605056
};
50615057
}
50625058

5063-
function createUnaryTypeEraser(source: Type): TypeMapper {
5064-
return t => t === source ? anyType : t;
5065-
}
5066-
5067-
function createBinaryTypeEraser(source1: Type, source2: Type): TypeMapper {
5068-
return t => t === source1 || t === source2 ? anyType : t;
5059+
function createTypeMapper(sources: Type[], targets: Type[]): TypeMapper {
5060+
const count = sources.length;
5061+
const mapper: TypeMapper =
5062+
count == 1 ? createUnaryTypeMapper(sources[0], targets ? targets[0] : anyType) :
5063+
count == 2 ? createBinaryTypeMapper(sources[0], targets ? targets[0] : anyType, sources[1], targets ? targets[1] : anyType) :
5064+
createArrayTypeMapper(sources, targets);
5065+
mapper.mappedTypes = sources;
5066+
return mapper;
50695067
}
50705068

50715069
function createTypeEraser(sources: Type[]): TypeMapper {
5072-
switch (sources.length) {
5073-
case 1: return createUnaryTypeEraser(sources[0]);
5074-
case 2: return createBinaryTypeEraser(sources[0], sources[1]);
5075-
}
5076-
return t => {
5077-
for (const source of sources) {
5078-
if (t === source) {
5079-
return anyType;
5080-
}
5081-
}
5082-
return t;
5083-
};
5070+
return createTypeMapper(sources, undefined);
50845071
}
50855072

50865073
function getInferenceMapper(context: InferenceContext): TypeMapper {
@@ -5095,6 +5082,7 @@ namespace ts {
50955082
}
50965083
return t;
50975084
};
5085+
mapper.mappedTypes = context.typeParameters;
50985086
mapper.context = context;
50995087
context.mapper = mapper;
51005088
}
@@ -5106,7 +5094,9 @@ namespace ts {
51065094
}
51075095

51085096
function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper {
5109-
return t => instantiateType(mapper1(t), mapper2);
5097+
const mapper: TypeMapper = t => instantiateType(mapper1(t), mapper2);
5098+
mapper.mappedTypes = mapper1.mappedTypes;
5099+
return mapper;
51105100
}
51115101

51125102
function cloneTypeParameter(typeParameter: TypeParameter): TypeParameter {
@@ -5201,13 +5191,70 @@ namespace ts {
52015191
return result;
52025192
}
52035193

5194+
function isSymbolInScopeOfMappedTypeParameter(symbol: Symbol, mapper: TypeMapper) {
5195+
const mappedTypes = mapper.mappedTypes;
5196+
// Starting with the parent of the symbol's declaration, check if the mapper maps any of
5197+
// the type parameters introduced by enclosing declarations. We just pick the first
5198+
// declaration since multiple declarations will all have the same parent anyway.
5199+
let node = symbol.declarations[0].parent;
5200+
while (node) {
5201+
switch (node.kind) {
5202+
case SyntaxKind.FunctionType:
5203+
case SyntaxKind.ConstructorType:
5204+
case SyntaxKind.FunctionDeclaration:
5205+
case SyntaxKind.MethodDeclaration:
5206+
case SyntaxKind.MethodSignature:
5207+
case SyntaxKind.Constructor:
5208+
case SyntaxKind.CallSignature:
5209+
case SyntaxKind.ConstructSignature:
5210+
case SyntaxKind.IndexSignature:
5211+
case SyntaxKind.GetAccessor:
5212+
case SyntaxKind.SetAccessor:
5213+
case SyntaxKind.FunctionExpression:
5214+
case SyntaxKind.ArrowFunction:
5215+
case SyntaxKind.ClassDeclaration:
5216+
case SyntaxKind.ClassExpression:
5217+
case SyntaxKind.InterfaceDeclaration:
5218+
case SyntaxKind.TypeAliasDeclaration:
5219+
const declaration = <DeclarationWithTypeParameters>node;
5220+
if (declaration.typeParameters) {
5221+
for (const d of declaration.typeParameters) {
5222+
if (contains(mappedTypes, getDeclaredTypeOfTypeParameter(d.symbol))) {
5223+
return true;
5224+
}
5225+
}
5226+
}
5227+
if (isClassLike(node) || node.kind === SyntaxKind.InterfaceDeclaration) {
5228+
const thisType = getDeclaredTypeOfClassOrInterface(getSymbolOfNode(node)).thisType;
5229+
if (thisType && contains(mappedTypes, thisType)) {
5230+
return true;
5231+
}
5232+
}
5233+
break;
5234+
case SyntaxKind.ModuleDeclaration:
5235+
case SyntaxKind.SourceFile:
5236+
return false;
5237+
}
5238+
node = node.parent;
5239+
}
5240+
return false;
5241+
}
5242+
52045243
function instantiateType(type: Type, mapper: TypeMapper): Type {
52055244
if (type && mapper !== identityMapper) {
52065245
if (type.flags & TypeFlags.TypeParameter) {
52075246
return mapper(<TypeParameter>type);
52085247
}
52095248
if (type.flags & TypeFlags.Anonymous) {
5210-
return type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) ?
5249+
// If the anonymous type originates in a declaration of a function, method, class, or
5250+
// interface, in an object type literal, or in an object literal expression, we may need
5251+
// to instantiate the type because it might reference a type parameter. We skip instantiation
5252+
// if none of the type parameters that are in scope in the type's declaration are mapped by
5253+
// the given mapper, however we can only do that analysis if the type isn't itself an
5254+
// instantiation.
5255+
return type.symbol &&
5256+
type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) &&
5257+
(type.flags & TypeFlags.Instantiated || isSymbolInScopeOfMappedTypeParameter(type.symbol, mapper)) ?
52115258
instantiateAnonymousType(<AnonymousType>type, mapper) : type;
52125259
}
52135260
if (type.flags & TypeFlags.Reference) {

src/compiler/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,6 +1208,8 @@ namespace ts {
12081208
block: Block;
12091209
}
12101210

1211+
export type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration;
1212+
12111213
export interface ClassLikeDeclaration extends Declaration {
12121214
name?: Identifier;
12131215
typeParameters?: NodeArray<TypeParameterDeclaration>;
@@ -2286,6 +2288,7 @@ namespace ts {
22862288
/* @internal */
22872289
export interface TypeMapper {
22882290
(t: TypeParameter): Type;
2291+
mappedTypes?: Type[]; // Types mapped by this mapper
22892292
instantiations?: Type[]; // Cache of instantiations created using this type mapper.
22902293
context?: InferenceContext; // The inference context this mapper was created from.
22912294
// Only inference mappers have this set (in createInferenceMapper).

0 commit comments

Comments
 (0)