Optimize type inference

This commit is contained in:
Anders Hejlsberg 2016-07-25 09:05:29 -07:00
parent ff0cbb5b29
commit a1a8725456
2 changed files with 24 additions and 10 deletions

View file

@ -7393,6 +7393,24 @@ namespace ts {
};
}
// Return true if the given type could possibly reference a type parameter for which
// we perform type inference (i.e. a type parameter of a generic function). We cache
// results for union and intersection types for performance reasons.
function couldContainTypeParameters(type: Type): boolean {
return !!(type.flags & TypeFlags.TypeParameter ||
type.flags & TypeFlags.Reference && forEach((<TypeReference>type).typeArguments, couldContainTypeParameters) ||
type.flags & TypeFlags.Tuple && forEach((<TupleType>type).elementTypes, couldContainTypeParameters) ||
type.flags & TypeFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) ||
type.flags & TypeFlags.UnionOrIntersection && couldUnionOrIntersectionContainTypeParameters(<UnionOrIntersectionType>type));
}
function couldUnionOrIntersectionContainTypeParameters(type: UnionOrIntersectionType): boolean {
if (type.couldContainTypeParameters === undefined) {
type.couldContainTypeParameters = forEach(type.types, couldContainTypeParameters);
}
return type.couldContainTypeParameters;
}
function inferTypes(context: InferenceContext, source: Type, target: Type) {
let sourceStack: Type[];
let targetStack: Type[];
@ -7411,6 +7429,9 @@ namespace ts {
}
function inferFromTypes(source: Type, target: Type) {
if (!couldContainTypeParameters(target)) {
return;
}
if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union && !(source.flags & TypeFlags.Enum && target.flags & TypeFlags.Enum) ||
source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) {
// Source and target are both unions or both intersections. If source and target
@ -7521,25 +7542,18 @@ namespace ts {
}
else {
source = getApparentType(source);
if (source.flags & TypeFlags.ObjectType && (
target.flags & TypeFlags.Reference && (<TypeReference>target).typeArguments ||
target.flags & TypeFlags.Tuple ||
target.flags & TypeFlags.Anonymous && target.symbol && target.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class))) {
// If source is an object type, and target is a type reference with type arguments, a tuple type,
// the type of a method, or a type literal, infer from members
if (source.flags & TypeFlags.ObjectType) {
if (isInProcess(source, target)) {
return;
}
if (isDeeplyNestedGeneric(source, sourceStack, depth) && isDeeplyNestedGeneric(target, targetStack, depth)) {
return;
}
const key = source.id + "," + target.id;
if (hasProperty(visited, key)) {
return;
}
visited[key] = true;
if (depth === 0) {
sourceStack = [];
targetStack = [];

View file

@ -2364,9 +2364,9 @@ namespace ts {
export interface UnionOrIntersectionType extends Type {
types: Type[]; // Constituent types
/* @internal */
reducedType: Type; // Reduced union type (all subtypes removed)
/* @internal */
resolvedProperties: SymbolTable; // Cache of resolved properties
/* @internal */
couldContainTypeParameters: boolean;
}
export interface UnionType extends UnionOrIntersectionType { }