From 9c4190b1fb31b6317efc15430c2047ca78779ff0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 6 Oct 2016 13:25:42 -0700 Subject: [PATCH] Introduce sameMap function --- src/compiler/checker.ts | 26 ++++++++++++++++---------- src/compiler/core.ts | 24 ++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index eff52967544..414a92ffed4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7404,7 +7404,7 @@ namespace ts { type.flags & TypeFlags.NumberLiteral ? numberType : type.flags & TypeFlags.BooleanLiteral ? booleanType : type.flags & TypeFlags.EnumLiteral ? (type).baseType : - type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Enum) ? getUnionType(map((type).types, getBaseTypeOfLiteralType)) : + type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Enum) ? getUnionType(sameMap((type).types, getBaseTypeOfLiteralType)) : type; } @@ -7413,7 +7413,7 @@ namespace ts { type.flags & TypeFlags.NumberLiteral && type.flags & TypeFlags.FreshLiteral ? numberType : type.flags & TypeFlags.BooleanLiteral ? booleanType : type.flags & TypeFlags.EnumLiteral ? (type).baseType : - type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Enum) ? getUnionType(map((type).types, getWidenedLiteralType)) : + type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Enum) ? getUnionType(sameMap((type).types, getWidenedLiteralType)) : type; } @@ -7552,10 +7552,10 @@ namespace ts { return getWidenedTypeOfObjectLiteral(type); } if (type.flags & TypeFlags.Union) { - return getUnionType(map((type).types, getWidenedConstituentType)); + return getUnionType(sameMap((type).types, getWidenedConstituentType)); } if (isArrayType(type) || isTupleType(type)) { - return createTypeReference((type).target, map((type).typeArguments, getWidenedType)); + return createTypeReference((type).target, sameMap((type).typeArguments, getWidenedType)); } } return type; @@ -7973,7 +7973,7 @@ namespace ts { const widenLiteralTypes = context.inferences[index].topLevel && !hasPrimitiveConstraint(signature.typeParameters[index]) && (context.inferences[index].isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), signature.typeParameters[index])); - const baseInferences = widenLiteralTypes ? map(inferences, getWidenedLiteralType) : inferences; + const baseInferences = widenLiteralTypes ? sameMap(inferences, getWidenedLiteralType) : inferences; // Infer widened union or supertype, or the unknown type for no common supertype const unionOrSuperType = context.inferUnionTypes ? getUnionType(baseInferences, /*subtypeReduction*/ true) : getCommonSupertype(baseInferences); inferredType = unionOrSuperType ? getWidenedType(unionOrSuperType) : unknownType; @@ -8485,9 +8485,15 @@ namespace ts { return type.flags & TypeFlags.Anonymous && !!(type).elementType; } + function createFinalArrayType(elementType: Type) { + return createArrayType(elementType !== neverType ? + getUnionType([elementType], /*subtypeReduction*/ true) : + strictNullChecks ? neverType : undefinedWideningType); + } + // We perform subtype reduction upon obtaining the final array type from an evolving array type. function getFinalArrayType(evolvingArrayType: AnonymousType): Type { - return evolvingArrayType.finalArrayType || (evolvingArrayType.finalArrayType = createArrayType(getUnionType([evolvingArrayType.elementType], /*subtypeReduction*/ true))); + return evolvingArrayType.finalArrayType || (evolvingArrayType.finalArrayType = createFinalArrayType(evolvingArrayType.elementType)); } function finalizeEvolvingArrayType(type: Type): Type { @@ -8504,7 +8510,7 @@ namespace ts { function getUnionOrEvolvingArrayType(types: Type[], subtypeReduction: boolean) { return types.length && every(types, isEvolvingArrayType) ? getEvolvingArrayType(getUnionType(map(types, getElementTypeOfEvolvingArrayType))) : - getUnionType(map(types, finalizeEvolvingArrayType), subtypeReduction); + getUnionType(sameMap(types, finalizeEvolvingArrayType), subtypeReduction); } // Return true if the given node is 'x' in an 'x.push(value)' operation. @@ -8754,7 +8760,7 @@ namespace ts { // junction is always the non-looping control flow path that leads to the top. for (let i = flowLoopStart; i < flowLoopCount; i++) { if (flowLoopNodes[i] === flow && flowLoopKeys[i] === key) { - return createFlowType(getUnionOrEvolvingArrayType(flowLoopTypes[i], false), /*incomplete*/ true); + return createFlowType(getUnionOrEvolvingArrayType(flowLoopTypes[i], /*subtypeReduction*/ false), /*incomplete*/ true); } } // Add the flow loop junction and reference to the in-process stack and analyze @@ -10728,7 +10734,7 @@ namespace ts { } } - return getUnionType(signatures.map(getReturnTypeOfSignature), /*subtypeReduction*/ true); + return getUnionType(map(signatures, getReturnTypeOfSignature), /*subtypeReduction*/ true); } /// e.g. "props" for React.d.ts, @@ -10778,7 +10784,7 @@ namespace ts { } if (elemType.flags & TypeFlags.Union) { const types = (elemType).types; - return getUnionType(types.map(type => { + return getUnionType(map(types, type => { return getResolvedJsxType(node, type, elemClassType); }), /*subtypeReduction*/ true); } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 72d05e1cfd7..bb3aaf23259 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -257,13 +257,33 @@ namespace ts { if (array) { result = []; for (let i = 0; i < array.length; i++) { - const v = array[i]; - result.push(f(v, i)); + result.push(f(array[i], i)); } } return result; } + // Maps from T to T and avoids allocation of all elements map to themselves + export function sameMap(array: T[], f: (x: T, i: number) => T): T[] { + let result: T[]; + if (array) { + for (let i = 0; i < array.length; i++) { + if (result) { + result.push(f(array[i], i)); + } + else { + const item = array[i]; + const mapped = f(item, i); + if (item !== mapped) { + result = array.slice(0, i); + result.push(mapped); + } + } + } + } + return result || array; + } + /** * Flattens an array containing a mix of array or non-array elements. *