From a3ffe6f874cdd7686013202f76962c8c7d8d74af Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 26 Sep 2016 09:30:15 -0700 Subject: [PATCH 001/224] Parse, bind and check spread types and elements --- src/compiler/binder.ts | 12 +- src/compiler/checker.ts | 407 ++++++++++++++++++++++----- src/compiler/diagnosticMessages.json | 12 + src/compiler/parser.ts | 29 +- src/compiler/types.ts | 92 +++--- src/compiler/utilities.ts | 1 + src/harness/harness.ts | 2 +- src/services/findAllReferences.ts | 6 +- 8 files changed, 447 insertions(+), 114 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 49434e80abab4..d3217fd70a850 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1825,8 +1825,9 @@ namespace ts { case SyntaxKind.EnumMember: return bindPropertyOrMethodOrAccessor(node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes); + case SyntaxKind.SpreadElement: case SyntaxKind.JsxSpreadAttribute: - emitFlags |= NodeFlags.HasJsxSpreadAttributes; + emitFlags |= NodeFlags.HasSpreadAttribute; return; case SyntaxKind.CallSignature: @@ -2956,8 +2957,9 @@ namespace ts { } break; + case SyntaxKind.SpreadElement: case SyntaxKind.SpreadElementExpression: - // This node is ES6 syntax, but is handled by a containing node. + // This node is ES6 or ES future syntax, but is handled by a containing node. transformFlags |= TransformFlags.ContainsSpreadElementExpression; break; @@ -2996,6 +2998,12 @@ namespace ts { transformFlags |= TransformFlags.ContainsLexicalThis; } + if (subtreeFlags & TransformFlags.ContainsSpreadElementExpression) { + // If an ObjectLiteralExpression contains a spread element, then it + // is an ES experimental node. + transformFlags |= TransformFlags.AssertExperimental; + } + break; case SyntaxKind.ArrayLiteralExpression: diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 51550ef873fc5..3578aeb5e4bb9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -112,6 +112,7 @@ namespace ts { const tupleTypes: GenericType[] = []; const unionTypes = createMap(); const intersectionTypes = createMap(); + const spreadTypes = createMap(); const stringLiteralTypes = createMap(); const numericLiteralTypes = createMap(); @@ -2185,7 +2186,10 @@ namespace ts { writeSymbolTypeReference(type.aliasSymbol, typeArguments, 0, typeArguments ? typeArguments.length : 0, nextFlags); } else if (type.flags & TypeFlags.UnionOrIntersection) { - writeUnionOrIntersectionType(type, nextFlags); + writeUnionOrIntersectionType(type, nextFlags); + } + else if (type.flags & TypeFlags.Spread) { + writeSpreadType(type, nextFlags); } else if (type.flags & TypeFlags.Anonymous) { writeAnonymousType(type, nextFlags); @@ -2279,7 +2283,7 @@ namespace ts { } } - function writeUnionOrIntersectionType(type: UnionOrIntersectionType, flags: TypeFormatFlags) { + function writeUnionOrIntersectionType(type: TypeOperatorType, flags: TypeFormatFlags) { if (flags & TypeFormatFlags.InElementType) { writePunctuation(writer, SyntaxKind.OpenParenToken); } @@ -2294,6 +2298,36 @@ namespace ts { } } + function writeSpreadType(type: SpreadType, flags: TypeFormatFlags) { + writePunctuation(writer, SyntaxKind.OpenBraceToken); + writer.writeLine(); + writer.increaseIndent(); + let printFollowingPunctuation = false; + for (const t of type.types) { + if (printFollowingPunctuation) { + writePunctuation(writer, SyntaxKind.SemicolonToken); + writer.writeLine(); + } + if (t.isDeclaredProperty) { + const saveInObjectTypeLiteral = inObjectTypeLiteral; + inObjectTypeLiteral = true; + writeObjectLiteralType(resolveStructuredTypeMembers(t)); + printFollowingPunctuation = false; + inObjectTypeLiteral = saveInObjectTypeLiteral; + } + else { + writePunctuation(writer, SyntaxKind.DotDotDotToken); + writeType(t, TypeFormatFlags.None); + printFollowingPunctuation = true; + } + } + writer.decreaseIndent(); + if (printFollowingPunctuation) { + writeSpace(writer); + } + writePunctuation(writer, SyntaxKind.CloseBraceToken); + } + function writeAnonymousType(type: ObjectType, flags: TypeFormatFlags) { const symbol = type.symbol; if (symbol) { @@ -2436,6 +2470,13 @@ namespace ts { writePunctuation(writer, SyntaxKind.OpenBraceToken); writer.writeLine(); writer.increaseIndent(); + writeObjectLiteralType(resolved); + writer.decreaseIndent(); + writePunctuation(writer, SyntaxKind.CloseBraceToken); + inObjectTypeLiteral = saveInObjectTypeLiteral; + } + + function writeObjectLiteralType(resolved: ResolvedType) { for (const signature of resolved.callSignatures) { buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, /*kind*/ undefined, symbolStack); writePunctuation(writer, SyntaxKind.SemicolonToken); @@ -2468,11 +2509,8 @@ namespace ts { writer.writeLine(); } } - writer.decreaseIndent(); - writePunctuation(writer, SyntaxKind.CloseBraceToken); - inObjectTypeLiteral = saveInObjectTypeLiteral; } - } + } function buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags) { const targetSymbol = getTargetSymbol(symbol); @@ -2981,7 +3019,11 @@ namespace ts { // present (aka the tuple element property). This call also checks that the parentType is in // fact an iterable or array (depending on target language). const elementType = checkIteratedTypeOrElementType(parentType, pattern, /*allowStringInput*/ false); - if (!declaration.dotDotDotToken) { + if (declaration.dotDotDotToken) { + // Rest element has an array type with the same element type as the parent type + type = createArrayType(elementType); + } + else { // Use specific property type when parent is a tuple or numeric index type when parent is an array const propName = "" + indexOf(pattern.elements, declaration); type = isTupleLikeType(parentType) @@ -2997,10 +3039,6 @@ namespace ts { return unknownType; } } - else { - // Rest element has an array type with the same element type as the parent type - type = createArrayType(elementType); - } } // In strict null checking mode, if a default value of a non-undefined type is specified, remove // undefined from the final type. @@ -4266,6 +4304,22 @@ namespace ts { setObjectTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); } + function resolveSpreadTypeMembers(type: SpreadType) { + // The members and properties collections are empty for spread types. To get all properties of an + // spread type use getPropertiesOfType (only the language service uses this). + let stringIndexInfo: IndexInfo = undefined; + let numberIndexInfo: IndexInfo = undefined; + for (let i = type.types.length - 1; i > -1; i--) { + const t = type.types[i]; + stringIndexInfo = intersectIndexInfos(stringIndexInfo, getIndexInfoOfType(t, IndexKind.String)); + numberIndexInfo = intersectIndexInfos(numberIndexInfo, getIndexInfoOfType(t, IndexKind.Number)); + if (!t.symbol || !(t.symbol.flags & SymbolFlags.Optional)) { + break; + } + } + setObjectTypeMembers(type, emptySymbols, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); + } + function resolveAnonymousTypeMembers(type: AnonymousType) { const symbol = type.symbol; if (type.target) { @@ -4332,6 +4386,9 @@ namespace ts { else if (type.flags & TypeFlags.Intersection) { resolveIntersectionTypeMembers(type); } + else if (type.flags & TypeFlags.Spread) { + resolveSpreadTypeMembers(type); + } } return type; } @@ -4341,6 +4398,9 @@ namespace ts { if (type.flags & TypeFlags.ObjectType) { return resolveStructuredTypeMembers(type).properties; } + if (type.flags & TypeFlags.Spread) { + return getPropertiesOfType(type); + } return emptyArray; } @@ -4356,10 +4416,11 @@ namespace ts { } } - function getPropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType): Symbol[] { + function getPropertiesOfUnionOrIntersectionOrSpreadType(type: TypeOperatorType): Symbol[] { + const getProperty = type.flags & TypeFlags.Spread ? getPropertyOfSpreadType : getPropertyOfUnionOrIntersectionType; for (const current of type.types) { for (const prop of getPropertiesOfType(current)) { - getPropertyOfUnionOrIntersectionType(type, prop.name); + getProperty(type, prop.name); } // The properties of a union type are those that are present in all constituent types, so // we only need to check the properties of the first type @@ -4372,7 +4433,8 @@ namespace ts { function getPropertiesOfType(type: Type): Symbol[] { type = getApparentType(type); - return type.flags & TypeFlags.UnionOrIntersection ? getPropertiesOfUnionOrIntersectionType(type) : getPropertiesOfObjectType(type); + return type.flags & (TypeFlags.UnionOrIntersection | TypeFlags.Spread) ? getPropertiesOfUnionOrIntersectionOrSpreadType(type) : + getPropertiesOfObjectType(type); } /** @@ -4398,7 +4460,7 @@ namespace ts { function getApparentType(type: Type): Type { if (type.flags & TypeFlags.TypeParameter) { type = getApparentTypeOfTypeParameter(type); - } + } if (type.flags & TypeFlags.StringLike) { type = globalStringType; } @@ -4414,34 +4476,43 @@ namespace ts { return type; } - function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: string): Symbol { + function createUnionOrIntersectionProperty(containingType: TypeOperatorType, name: string): Symbol { const types = containingType.types; - let props: Symbol[]; - // Flags we want to propagate to the result if they exist in all source symbols - let commonFlags = (containingType.flags & TypeFlags.Intersection) ? SymbolFlags.Optional : SymbolFlags.None; - let isReadonly = false; - for (const current of types) { - const type = getApparentType(current); - if (type !== unknownType) { - const prop = getPropertyOfType(type, name); - if (prop && !(getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected))) { - commonFlags &= prop.flags; - if (!props) { - props = [prop]; - } - else if (!contains(props, prop)) { - props.push(prop); + return createUnionOrIntersectionOrSpreadPropertySymbol(containingType, name, () => { + let props: Symbol[]; + // Flags we want to propagate to the result if they exist in all source symbols + let commonFlags = (containingType.flags & TypeFlags.Intersection) ? SymbolFlags.Optional : SymbolFlags.None; + let isReadonly = false; + for (const current of types) { + const type = getApparentType(current); + if (type !== unknownType) { + const prop = getPropertyOfType(type, name); + if (prop && !(getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected))) { + commonFlags &= prop.flags; + if (!props) { + props = [prop]; + } + else if (!contains(props, prop)) { + props.push(prop); + } + if (isReadonlySymbol(prop)) { + isReadonly = true; + } } - if (isReadonlySymbol(prop)) { - isReadonly = true; + else if (containingType.flags & TypeFlags.Union) { + // A union type requires the property to be present in all constituent types + return [undefined, false, 0]; } } - else if (containingType.flags & TypeFlags.Union) { - // A union type requires the property to be present in all constituent types - return undefined; - } } - } + return [props, isReadonly, commonFlags]; + }); + } + + function createUnionOrIntersectionOrSpreadPropertySymbol(containingType: TypeOperatorType, + name: string, + symbolCreator: () => [Symbol[], boolean, SymbolFlags]) { + const [props, isReadonly, flags] = symbolCreator(); if (!props) { return undefined; } @@ -4469,17 +4540,17 @@ namespace ts { SymbolFlags.Property | SymbolFlags.Transient | SymbolFlags.SyntheticProperty | - commonFlags, + flags, name); result.containingType = containingType; result.hasCommonType = hasCommonType; result.declarations = declarations; result.isReadonly = isReadonly; - result.type = containingType.flags & TypeFlags.Union ? getUnionType(propTypes) : getIntersectionType(propTypes); + result.type = containingType.flags & TypeFlags.Intersection ? getIntersectionType(propTypes) : getUnionType(propTypes); return result; } - function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: string): Symbol { + function getPropertyOfUnionOrIntersectionType(type: TypeOperatorType, name: string): Symbol { const properties = type.resolvedProperties || (type.resolvedProperties = createMap()); let property = properties[name]; if (!property) { @@ -4491,6 +4562,61 @@ namespace ts { return property; } + function createSpreadProperty(containingType: SpreadType, name: string): Symbol { + const types = containingType.types; + return createUnionOrIntersectionOrSpreadPropertySymbol(containingType, name, () => { + let props: Symbol[]; + // Result is readonly if any source is readonly + let isReadonly = false; + // Result is optional if all sources are optional + let commonFlags = SymbolFlags.Optional; + for (let i = types.length - 1; i > -1; i--) { + const type = getApparentType(types[i]); + if (type !== unknownType) { + const prop = getPropertyOfType(type, name); + if (prop) { + if (prop.flags & SymbolFlags.Method && !types[i].isDeclaredProperty || + prop.flags & SymbolFlags.SetAccessor && !(prop.flags & SymbolFlags.GetAccessor)) { + // skip non-object-literal methods and set-only properties and keep looking + continue; + } + if (!props) { + props = [prop]; + } + else if (!contains(props, prop)) { + props.unshift(prop); + } + if (isReadonlySymbol(prop)) { + isReadonly = true; + } + if (getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected)) { + // return immediately because even if prop is optional, it would make the unioned spread property private + return [undefined, false, 0]; + } + if (!(prop.flags & SymbolFlags.Optional)) { + // Reset extraFlags to None since we found a non-optional property + commonFlags = SymbolFlags.None; + break; + } + } + } + } + return [props, isReadonly, commonFlags]; + }); + } + + function getPropertyOfSpreadType(type: TypeOperatorType, name: string): Symbol { + const properties = type.resolvedProperties || (type.resolvedProperties = createMap()); + let property = properties[name]; + if (!property) { + property = createSpreadProperty(type as SpreadType, name); + if (property) { + properties[name] = property; + } + } + return property; + } + /** * Return the symbol for the property with the given name in the given type. Creates synthetic union properties when * necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from @@ -4516,7 +4642,10 @@ namespace ts { return getPropertyOfObjectType(globalObjectType, name); } if (type.flags & TypeFlags.UnionOrIntersection) { - return getPropertyOfUnionOrIntersectionType(type, name); + return getPropertyOfUnionOrIntersectionType(type, name); + } + if (type.flags & TypeFlags.Spread) { + return getPropertyOfSpreadType(type, name); } return undefined; } @@ -5538,8 +5667,44 @@ namespace ts { function getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node: Node, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { const links = getNodeLinks(node); if (!links.resolvedType) { - // Deferred resolution of members is handled by resolveObjectTypeMembers - const type = createObjectType(TypeFlags.Anonymous, node.symbol); + const isSpread = (node.kind === SyntaxKind.TypeLiteral && + find((node as TypeLiteralNode).members, elt => elt.kind === SyntaxKind.SpreadTypeElement)); + let type: ObjectType; + if (isSpread) { + let members: Map; + const spreads: SpreadElementType[] = []; + for (const member of (node as TypeLiteralNode).members) { + if (member.kind === SyntaxKind.SpreadTypeElement) { + if (members) { + const t = createAnonymousType(undefined, members, emptyArray, emptyArray, undefined, undefined) as SpreadElementType; + t.isDeclaredProperty = true; + spreads.push(t); + members = undefined; + } + spreads.push(getTypeFromTypeNode((member as SpreadTypeElement).type) as SpreadElementType); + } + else if (member.kind !== SyntaxKind.CallSignature && member.kind !== SyntaxKind.ConstructSignature) { + // note that spread types don't include call and construct signatures + const flags = SymbolFlags.Property | SymbolFlags.Transient | (member.questionToken ? SymbolFlags.Optional : 0); + const text = getTextOfPropertyName(member.name); + const symbol = createSymbol(flags, text); + symbol.type = getTypeFromTypeNodeNoAlias((member as IndexSignatureDeclaration | PropertySignature | MethodSignature).type); + if (!members) { + members = createMap(); + } + members[symbol.name] = symbol; + } + } + if (members) { + const t = createAnonymousType(undefined, members, emptyArray, emptyArray, undefined, undefined) as SpreadElementType; + t.isDeclaredProperty = true; + spreads.push(t); + } + return getSpreadType(spreads, node.symbol); + } + else { + type = createObjectType(TypeFlags.Anonymous, node.symbol); + } type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; links.resolvedType = type; @@ -5547,6 +5712,18 @@ namespace ts { return links.resolvedType; } + function getSpreadType(types: SpreadElementType[], symbol: Symbol, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { + const id = getTypeListId(types); + if (id in spreadTypes) { + return spreadTypes[id]; + } + const spread = spreadTypes[id] = createObjectType(TypeFlags.Spread, symbol) as SpreadType; + spread.types = filter(types, t => !(t.flags & (TypeFlags.Null | TypeFlags.Undefined))); + spread.aliasSymbol = aliasSymbol; + spread.aliasTypeArguments = aliasTypeArguments; + return spread; + } + function createLiteralType(flags: TypeFlags, text: string) { const type = createType(flags); type.text = text; @@ -5937,6 +6114,9 @@ namespace ts { if (type.flags & TypeFlags.Intersection) { return getIntersectionType(instantiateList((type).types, mapper, instantiateType), type.aliasSymbol, mapper.targetTypes); } + if (type.flags & TypeFlags.Spread) { + return getSpreadType(instantiateList((type as SpreadType).types, mapper, instantiateType) as SpreadElementType[], type.symbol, type.aliasSymbol, mapper.targetTypes); + } } return type; } @@ -6460,6 +6640,26 @@ namespace ts { } } + if (source.flags & TypeFlags.Spread && target.flags & TypeFlags.Spread) { + const sourceParameters = filter((source as SpreadType).types, t => !!(t.flags & TypeFlags.TypeParameter)); + const targetParameters = filter((target as SpreadType).types, t => !!(t.flags & TypeFlags.TypeParameter)); + if (sourceParameters.length !== targetParameters.length) { + reportRelationError(headMessage, source, target); + return Ternary.False; + } + for (let i = 0; i < sourceParameters.length; i++) { + if (sourceParameters[i].symbol !== targetParameters[i].symbol) { + reportRelationError(headMessage, source, target); + return Ternary.False; + } + } + const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo; + if (result = objectTypeRelatedTo(source, source, target, reportStructuralErrors)) { + errorInfo = saveErrorInfo; + return result; + } + } + if (source.flags & TypeFlags.TypeParameter) { let constraint = getConstraintOfTypeParameter(source); @@ -6490,7 +6690,7 @@ namespace ts { // In a check of the form X = A & B, we will have previously checked if A relates to X or B relates // to X. Failing both of those we want to check if the aggregation of A and B's members structurally // relates to X. Thus, we include intersection types on the source side here. - if (apparentSource.flags & (TypeFlags.ObjectType | TypeFlags.Intersection) && target.flags & TypeFlags.ObjectType) { + if (apparentSource.flags & (TypeFlags.ObjectType | TypeFlags.Intersection | TypeFlags.Spread) && target.flags & TypeFlags.ObjectType) { // Report structural errors only if we haven't reported any errors yet const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo && !(source.flags & TypeFlags.Primitive); if (result = objectTypeRelatedTo(apparentSource, source, target, reportStructuralErrors)) { @@ -6525,8 +6725,8 @@ namespace ts { } if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union || source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) { - if (result = eachTypeRelatedToSomeType(source, target, /*reportErrors*/ false)) { - if (result &= eachTypeRelatedToSomeType(target, source, /*reportErrors*/ false)) { + if (result = eachTypeRelatedToSomeType(source, target, /*reportErrors*/ false)) { + if (result &= eachTypeRelatedToSomeType(target, source, /*reportErrors*/ false)) { return result; } } @@ -6549,7 +6749,7 @@ namespace ts { } } else if (type.flags & TypeFlags.UnionOrIntersection) { - for (const t of (type).types) { + for (const t of (type).types) { if (isKnownProperty(t, name)) { return true; } @@ -6586,7 +6786,7 @@ namespace ts { return false; } - function eachTypeRelatedToSomeType(source: UnionOrIntersectionType, target: UnionOrIntersectionType, reportErrors: boolean): Ternary { + function eachTypeRelatedToSomeType(source: TypeOperatorType, target: TypeOperatorType, reportErrors: boolean): Ternary { let result = Ternary.True; const sourceTypes = source.types; for (const sourceType of sourceTypes) { @@ -6599,7 +6799,7 @@ namespace ts { return result; } - function typeRelatedToSomeType(source: Type, target: UnionOrIntersectionType, reportErrors: boolean): Ternary { + function typeRelatedToSomeType(source: Type, target: TypeOperatorType, reportErrors: boolean): Ternary { const targetTypes = target.types; if (target.flags & TypeFlags.Union && containsType(targetTypes, source)) { return Ternary.True; @@ -6614,7 +6814,7 @@ namespace ts { return Ternary.False; } - function typeRelatedToEachType(source: Type, target: UnionOrIntersectionType, reportErrors: boolean): Ternary { + function typeRelatedToEachType(source: Type, target: TypeOperatorType, reportErrors: boolean): Ternary { let result = Ternary.True; const targetTypes = target.types; for (const targetType of targetTypes) { @@ -6627,7 +6827,7 @@ namespace ts { return result; } - function someTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary { + function someTypeRelatedToType(source: TypeOperatorType, target: Type, reportErrors: boolean): Ternary { const sourceTypes = source.types; if (source.flags & TypeFlags.Union && containsType(sourceTypes, target)) { return Ternary.True; @@ -6642,7 +6842,7 @@ namespace ts { return Ternary.False; } - function eachTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary { + function eachTypeRelatedToType(source: TypeOperatorType, target: Type, reportErrors: boolean): Ternary { let result = Ternary.True; const sourceTypes = source.types; for (const sourceType of sourceTypes) { @@ -7578,10 +7778,10 @@ namespace ts { return !!(type.flags & TypeFlags.TypeParameter || type.flags & TypeFlags.Reference && forEach((type).typeArguments, couldContainTypeParameters) || type.flags & TypeFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) || - type.flags & TypeFlags.UnionOrIntersection && couldUnionOrIntersectionContainTypeParameters(type)); + type.flags & TypeFlags.UnionOrIntersection && couldUnionOrIntersectionContainTypeParameters(type)); } - function couldUnionOrIntersectionContainTypeParameters(type: UnionOrIntersectionType): boolean { + function couldUnionOrIntersectionContainTypeParameters(type: TypeOperatorType): boolean { if (type.couldContainTypeParameters === undefined) { type.couldContainTypeParameters = forEach(type.types, couldContainTypeParameters); } @@ -7589,7 +7789,7 @@ namespace ts { } function isTypeParameterAtTopLevel(type: Type, typeParameter: TypeParameter): boolean { - return type === typeParameter || type.flags & TypeFlags.UnionOrIntersection && forEach((type).types, t => isTypeParameterAtTopLevel(t, typeParameter)); + return type === typeParameter || type.flags & TypeFlags.UnionOrIntersection && forEach((type).types, t => isTypeParameterAtTopLevel(t, typeParameter)); } function inferTypes(context: InferenceContext, originalSource: Type, originalTarget: Type) { @@ -7619,7 +7819,7 @@ namespace ts { // Source and target are both unions or both intersections. If source and target // are the same type, just relate each constituent type to itself. if (source === target) { - for (const t of (source).types) { + for (const t of (source).types) { inferFromTypes(t, t); } return; @@ -7631,14 +7831,14 @@ namespace ts { // and string literals because the number and string types are not represented as unions // of all their possible values. let matchingTypes: Type[]; - for (const t of (source).types) { - if (typeIdenticalToSomeType(t, (target).types)) { + for (const t of (source).types) { + if (typeIdenticalToSomeType(t, (target).types)) { (matchingTypes || (matchingTypes = [])).push(t); inferFromTypes(t, t); } else if (t.flags & (TypeFlags.NumberLiteral | TypeFlags.StringLiteral)) { const b = getBaseTypeOfLiteralType(t); - if (typeIdenticalToSomeType(b, (target).types)) { + if (typeIdenticalToSomeType(b, (target).types)) { (matchingTypes || (matchingTypes = [])).push(t, b); } } @@ -7647,8 +7847,8 @@ namespace ts { // removing the identically matched constituents. For example, when inferring from // 'string | string[]' to 'string | T' we reduce the types to 'string[]' and 'T'. if (matchingTypes) { - source = removeTypesFromUnionOrIntersection(source, matchingTypes); - target = removeTypesFromUnionOrIntersection(target, matchingTypes); + source = removeTypesFromUnionOrIntersection(source, matchingTypes); + target = removeTypesFromUnionOrIntersection(target, matchingTypes); } } if (target.flags & TypeFlags.TypeParameter) { @@ -7695,7 +7895,7 @@ namespace ts { } } else if (target.flags & TypeFlags.UnionOrIntersection) { - const targetTypes = (target).types; + const targetTypes = (target).types; let typeParameterCount = 0; let typeParameter: TypeParameter; // First infer to each type in union or intersection that isn't a type parameter @@ -7719,7 +7919,7 @@ namespace ts { } else if (source.flags & TypeFlags.UnionOrIntersection) { // Source is a union or intersection type, infer from each constituent type - const sourceTypes = (source).types; + const sourceTypes = (source).types; for (const sourceType of sourceTypes) { inferFromTypes(sourceType, target); } @@ -7824,7 +8024,7 @@ namespace ts { * Return a new union or intersection type computed by removing a given set of types * from a given union or intersection type. */ - function removeTypesFromUnionOrIntersection(type: UnionOrIntersectionType, typesToRemove: Type[]) { + function removeTypesFromUnionOrIntersection(type: TypeOperatorType, typesToRemove: Type[]) { const reducedTypes: Type[] = []; for (const t of type.types) { if (!typeIdenticalToSomeType(t, typesToRemove)) { @@ -8148,7 +8348,7 @@ namespace ts { return getTypeFacts(constraint || emptyObjectType); } if (flags & TypeFlags.UnionOrIntersection) { - return getTypeFactsOfTypes((type).types); + return getTypeFactsOfTypes((type).types); } return TypeFacts.All; } @@ -10184,8 +10384,9 @@ namespace ts { // Grammar checking checkGrammarObjectLiteralExpression(node, inDestructuringPattern); - const propertiesTable = createMap(); - const propertiesArray: Symbol[] = []; + let propertiesTable = createMap(); + let propertiesArray: Symbol[] = []; + const spreads: SpreadElementType[] = []; const contextualType = getApparentTypeOfContextualType(node); const contextualTypeHasPattern = contextualType && contextualType.pattern && (contextualType.pattern.kind === SyntaxKind.ObjectBindingPattern || contextualType.pattern.kind === SyntaxKind.ObjectLiteralExpression); @@ -10210,6 +10411,14 @@ namespace ts { Debug.assert(memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment); type = checkExpressionForMutableLocation((memberDecl).name, contextualMapper); } + + if (hasProperty(propertiesTable, member.name)) { + const existingPropType = getTypeOfSymbol(propertiesTable[member.name]); + if (!isTypeIdenticalTo(existingPropType, type)) { + error(memberDecl.name, Diagnostics.Cannot_change_type_of_property_0_from_1_to_2, member.name, typeToString(existingPropType), typeToString(type)); + } + } + typeFlags |= type.flags; const prop = createSymbol(SymbolFlags.Property | SymbolFlags.Transient | member.flags, member.name); if (inDestructuringPattern) { @@ -10247,6 +10456,19 @@ namespace ts { prop.target = member; member = prop; } + else if (memberDecl.kind === SyntaxKind.SpreadElement) { + if (propertiesArray.length > 0) { + const t = createObjectLiteralType(node, hasComputedStringProperty, hasComputedNumberProperty, propertiesArray, propertiesTable, typeFlags, patternWithComputedProperties, inDestructuringPattern) as SpreadElementType; + t.isDeclaredProperty = true; + spreads.push(t); + propertiesArray = []; + propertiesTable = createMap(); + hasComputedStringProperty = false; + hasComputedNumberProperty = false; + } + spreads.push(checkExpression((memberDecl as SpreadElement).target) as SpreadElementType); + continue; + } else { // TypeScript 1.0 spec (April 2014) // A get accessor declaration is processed in the same manner as @@ -10286,6 +10508,22 @@ namespace ts { } } + if (spreads.length > 0) { + if (propertiesArray.length > 0) { + const t = createObjectLiteralType(node, hasComputedStringProperty, hasComputedNumberProperty, propertiesArray, propertiesTable, typeFlags, patternWithComputedProperties, inDestructuringPattern) as SpreadElementType; + t.isDeclaredProperty = true; + spreads.push(t); + } + const propagatedFlags = getPropagatingFlagsOfTypes(spreads, /*excludeKinds*/ TypeFlags.Nullable); + const spread = getSpreadType(spreads, node.symbol); + spread.flags |= propagatedFlags; + return spread; + } + + return createObjectLiteralType(node, hasComputedStringProperty, hasComputedNumberProperty, propertiesArray, propertiesTable, typeFlags, patternWithComputedProperties, inDestructuringPattern); + } + + function createObjectLiteralType(node: ObjectLiteralExpression, hasComputedStringProperty: boolean, hasComputedNumberProperty: boolean, propertiesArray: Symbol[], propertiesTable: Map, typeFlags: TypeFlags, patternWithComputedProperties: boolean, inDestructuringPattern: boolean) { const stringIndexInfo = hasComputedStringProperty ? getObjectLiteralIndexInfo(node, propertiesArray, IndexKind.String) : undefined; const numberIndexInfo = hasComputedNumberProperty ? getObjectLiteralIndexInfo(node, propertiesArray, IndexKind.Number) : undefined; const result = createAnonymousType(node.symbol, propertiesTable, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); @@ -10534,7 +10772,7 @@ namespace ts { elemType = checkExpression(node.tagName); } if (elemType.flags & TypeFlags.Union) { - const types = (elemType).types; + const types = (elemType).types; return getUnionType(types.map(type => { return getResolvedJsxType(node, type, elemClassType); }), /*subtypeReduction*/ true); @@ -13096,7 +13334,7 @@ namespace ts { return true; } if (type.flags & TypeFlags.UnionOrIntersection) { - const types = (type).types; + const types = (type).types; for (const t of types) { if (maybeTypeOfKind(t, kind)) { return true; @@ -13114,7 +13352,7 @@ namespace ts { return true; } if (type.flags & TypeFlags.Union) { - const types = (type).types; + const types = (type).types; for (const t of types) { if (!isTypeOfKind(t, kind)) { return false; @@ -13123,7 +13361,7 @@ namespace ts { return true; } if (type.flags & TypeFlags.Intersection) { - const types = (type).types; + const types = (type).types; for (const t of types) { if (isTypeOfKind(t, kind)) { return true; @@ -19194,7 +19432,7 @@ namespace ts { if (requestedExternalEmitHelpers & NodeFlags.HasClassExtends && languageVersion < ScriptTarget.ES6) { verifyHelperSymbol(exports, "__extends", SymbolFlags.Value); } - if (requestedExternalEmitHelpers & NodeFlags.HasJsxSpreadAttributes && compilerOptions.jsx !== JsxEmit.Preserve) { + if (requestedExternalEmitHelpers & NodeFlags.HasSpreadAttribute && compilerOptions.jsx !== JsxEmit.Preserve) { verifyHelperSymbol(exports, "__assign", SymbolFlags.Value); } if (requestedExternalEmitHelpers & NodeFlags.HasDecorators) { @@ -19758,6 +19996,11 @@ namespace ts { } } + let result: TypeElement; + if (result = find(node.members, e => e.kind === SyntaxKind.SpreadTypeElement)) { + return grammarErrorOnNode(result, Diagnostics.Interface_declaration_cannot_contain_a_spread_property); + } + return false; } @@ -19805,9 +20048,23 @@ namespace ts { const GetOrSetAccessor = GetAccessor | SetAccessor; for (const prop of node.properties) { + if (prop.kind === SyntaxKind.SpreadElement) { + const target = (prop as SpreadElement).target; + switch (target.kind) { + case SyntaxKind.Identifier: + case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.NullKeyword: + break; + default: + grammarErrorOnNode(target, Diagnostics.Spread_properties_must_be_identifiers_property_accesses_or_object_literals); + } + + continue; + } const name = prop.name; if (prop.kind === SyntaxKind.OmittedExpression || - name.kind === SyntaxKind.ComputedPropertyName) { + name && name.kind === SyntaxKind.ComputedPropertyName) { // If the name is not a ComputedPropertyName, the grammar checking will skip it checkGrammarComputedPropertyName(name); } @@ -20127,7 +20384,7 @@ namespace ts { } if (node.initializer) { - // Error on equals token which immediate precedes the initializer + // Error on equals token which immediately precedes the initializer return grammarErrorAtPos(getSourceFileOfNode(node), node.initializer.pos - 1, 1, Diagnostics.A_rest_element_cannot_have_an_initializer); } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index be4ee2fa07d98..69c2a431fc845 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1963,6 +1963,18 @@ "category": "Error", "code": 2696 }, + "Spread properties must be identifiers, property accesses, or object literals.": { + "category": "Error", + "code": 2697 + }, + "Cannot change type of property '{0}' from '{1}' to '{2}'.": { + "category": "Error", + "code": 2698 + }, + "Interface declaration cannot contain a spread property.": { + "category": "Error", + "code": 2699 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 1aec63bc588ca..ae88b6d948b5b 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -74,6 +74,11 @@ namespace ts { visitNode(cbNode, (node).questionToken) || visitNode(cbNode, (node).equalsToken) || visitNode(cbNode, (node).objectAssignmentInitializer); + case SyntaxKind.SpreadElement: + return visitNode(cbNode, (node).dotDotDotToken) || + visitNode(cbNode, (node).target); + case SyntaxKind.SpreadTypeElement: + return visitNode(cbNode, (node as SpreadTypeElement).type); case SyntaxKind.Parameter: case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: @@ -1262,7 +1267,7 @@ namespace ts { // which would be a candidate for improved error reporting. return token() === SyntaxKind.OpenBracketToken || isLiteralPropertyName(); case ParsingContext.ObjectLiteralMembers: - return token() === SyntaxKind.OpenBracketToken || token() === SyntaxKind.AsteriskToken || isLiteralPropertyName(); + return token() === SyntaxKind.OpenBracketToken || token() === SyntaxKind.AsteriskToken || token() === SyntaxKind.DotDotDotToken || isLiteralPropertyName(); case ParsingContext.ObjectBindingElements: return token() === SyntaxKind.OpenBracketToken || isLiteralPropertyName(); case ParsingContext.HeritageClauseElement: @@ -2328,6 +2333,10 @@ namespace ts { if (token() === SyntaxKind.OpenBracketToken) { return true; } + // spread elements are type members + if (token() === SyntaxKind.DotDotDotToken) { + return true; + } // Try to get the first property-like token following all modifiers if (isLiteralPropertyName()) { idToken = token(); @@ -2353,6 +2362,9 @@ namespace ts { if (token() === SyntaxKind.NewKeyword && lookAhead(isStartOfConstructSignature)) { return parseSignatureMember(SyntaxKind.ConstructSignature); } + if (token() === SyntaxKind.DotDotDotToken) { + return parseSpreadTypeElement(); + } const fullStart = getNodePos(); const modifiers = parseModifiers(); if (isIndexSignature()) { @@ -2361,6 +2373,14 @@ namespace ts { return parsePropertyOrMethodSignature(fullStart, modifiers); } + function parseSpreadTypeElement() { + const element = createNode(SyntaxKind.SpreadTypeElement, scanner.getStartPos()) as SpreadTypeElement; + parseTokenNode(); // parse `...` + element.type = parseType(); + parseTypeMemberSemicolon(); + return finishNode(element); + } + function isStartOfConstructSignature() { nextToken(); return token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken; @@ -4123,6 +4143,13 @@ namespace ts { function parseObjectLiteralElement(): ObjectLiteralElement { const fullStart = scanner.getStartPos(); + const dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken); + if (dotDotDotToken) { + const spreadElement = createNode(SyntaxKind.SpreadElement, fullStart); + spreadElement.dotDotDotToken = dotDotDotToken; + spreadElement.target = parseAssignmentExpressionOrHigher(); + return addJSDocComment(finishNode(spreadElement)); + } const decorators = parseDecorators(); const modifiers = parseModifiers(); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e4bc9a29d5b03..cdf42ebea4ffc 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -316,6 +316,9 @@ namespace ts { // Property assignments PropertyAssignment, ShorthandPropertyAssignment, + SpreadElement, // maybe name it SpreadProperty? + SpreadTypeElement, // maybe name it SpreadTypeNode? + // Enum EnumMember, @@ -412,7 +415,7 @@ namespace ts { HasDecorators = 1 << 11, // If the file has decorators (initialized by binding) HasParamDecorators = 1 << 12, // If the file has parameter decorators (initialized by binding) HasAsyncFunctions = 1 << 13, // If the file has async functions (initialized by binding) - HasJsxSpreadAttributes = 1 << 14, // If the file as JSX spread attributes (initialized by binding) + HasSpreadAttribute = 1 << 14, // If the file as JSX spread attributes (initialized by binding) DisallowInContext = 1 << 15, // If node was parsed in a context where 'in-expressions' are not allowed YieldContext = 1 << 16, // If node was parsed in the 'yield' context created when parsing a generator DecoratorContext = 1 << 17, // If node was parsed as part of a decorator @@ -425,7 +428,7 @@ namespace ts { BlockScoped = Let | Const, ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn, - EmitHelperFlags = HasClassExtends | HasDecorators | HasParamDecorators | HasAsyncFunctions | HasJsxSpreadAttributes, + EmitHelperFlags = HasClassExtends | HasDecorators | HasParamDecorators | HasAsyncFunctions | HasSpreadAttribute, ReachabilityAndEmitFlags = ReachabilityCheckFlags | EmitHelperFlags, // Parsing context flags @@ -450,7 +453,6 @@ namespace ts { Async = 1 << 8, // Property/Method/Function Default = 1 << 9, // Function/Class (export default declaration) Const = 1 << 11, // Variable declaration - HasComputedFlags = 1 << 29, // Modifier flags have been computed AccessibilityModifier = Public | Private | Protected, @@ -635,6 +637,11 @@ namespace ts { initializer?: Expression; // Optional initializer } + // @kind(SyntaxKind.SpreadTypeElement) + export interface SpreadTypeElement extends TypeElement { + type: TypeNode; + } + // @kind(SyntaxKind.PropertyDeclaration) export interface PropertyDeclaration extends ClassElement { questionToken?: Node; // Present for use with reporting a grammar error @@ -666,6 +673,12 @@ namespace ts { objectAssignmentInitializer?: Expression; } + // @kind(SyntaxKind.SpreadElementExpression) + export interface SpreadElement extends ObjectLiteralElement { + dotDotDotToken: Node; + target: Expression; + } + // SyntaxKind.VariableDeclaration // SyntaxKind.Parameter // SyntaxKind.BindingElement @@ -2275,7 +2288,7 @@ namespace ts { instantiations?: Map; // Instantiations of generic type alias (undefined if non-generic) mapper?: TypeMapper; // Type mapper for instantiation alias referenced?: boolean; // True if alias symbol has been referenced as a value - containingType?: UnionOrIntersectionType; // Containing union or intersection type for synthetic property + containingType?: TypeOperatorType; // Containing union or intersection type for synthetic property hasCommonType?: boolean; // True if constituents of synthetic property all have same type isDiscriminantProperty?: boolean; // True if discriminant synthetic property resolvedExports?: SymbolTable; // Resolved exports of module @@ -2381,6 +2394,8 @@ namespace ts { ContainsAnyFunctionType = 1 << 27, // Type is or contains object literal type ThisType = 1 << 28, // This type ObjectLiteralPatternWithComputedProperties = 1 << 29, // Object literal type implied by binding pattern has computed properties + Spread = 1 << 30, // Spread types + // TODO: Move some types out to make room for Spread. /* @internal */ Nullable = Undefined | Null, @@ -2398,12 +2413,12 @@ namespace ts { EnumLike = Enum | EnumLiteral, ObjectType = Class | Interface | Reference | Tuple | Anonymous, UnionOrIntersection = Union | Intersection, - StructuredType = ObjectType | Union | Intersection, + StructuredType = ObjectType | Union | Intersection | Spread, StructuredOrTypeParameter = StructuredType | TypeParameter, // 'Narrowable' types are types where narrowing actually narrows. // This *should* be every type other than null, undefined, void, and never - Narrowable = Any | StructuredType | TypeParameter | StringLike | NumberLike | BooleanLike | ESSymbol, + Narrowable = Any | StructuredType | TypeParameter | StringLike | NumberLike | BooleanLike | ESSymbol | Spread, NotUnionOrUnit = Any | String | Number | ESSymbol | ObjectType, /* @internal */ RequiresWidening = ContainsWideningType | ContainsObjectLiteral, @@ -2486,7 +2501,7 @@ namespace ts { instantiations: Map; // Generic instantiation cache } - export interface UnionOrIntersectionType extends Type { + export interface TypeOperatorType extends Type { types: Type[]; // Constituent types /* @internal */ resolvedProperties: SymbolTable; // Cache of resolved properties @@ -2494,9 +2509,19 @@ namespace ts { couldContainTypeParameters: boolean; } - export interface UnionType extends UnionOrIntersectionType { } + export interface UnionType extends TypeOperatorType { } + + export interface IntersectionType extends TypeOperatorType { } - export interface IntersectionType extends UnionOrIntersectionType { } + /* @internal */ + export interface SpreadType extends TypeOperatorType { + types: SpreadElementType[]; // Constituent types + } + + /* @internal */ + export interface SpreadElementType extends ResolvedType { + isDeclaredProperty: boolean | undefined; + } /* @internal */ // An instantiated anonymous type has a target and a mapper @@ -2506,8 +2531,8 @@ namespace ts { } /* @internal */ - // Resolved object, union, or intersection type - export interface ResolvedType extends ObjectType, UnionOrIntersectionType { + // Resolved object, spread, union, or intersection type + export interface ResolvedType extends ObjectType, TypeOperatorType { members: SymbolTable; // Properties by name properties: Symbol[]; // Properties callSignatures: Signature[]; // Call signatures of type @@ -3096,29 +3121,31 @@ namespace ts { ContainsTypeScript = 1 << 1, Jsx = 1 << 2, ContainsJsx = 1 << 3, - ES7 = 1 << 4, - ContainsES7 = 1 << 5, - ES6 = 1 << 6, - ContainsES6 = 1 << 7, - DestructuringAssignment = 1 << 8, - Generator = 1 << 9, - ContainsGenerator = 1 << 10, + Experimental = 1 << 4, + ContainsExperimental = 1 << 5, + ES7 = 1 << 6, + ContainsES7 = 1 << 7, + ES6 = 1 << 8, + ContainsES6 = 1 << 9, + DestructuringAssignment = 1 << 10, + Generator = 1 << 11, + ContainsGenerator = 1 << 12, // Markers // - Flags used to indicate that a subtree contains a specific transformation. - ContainsDecorators = 1 << 11, - ContainsPropertyInitializer = 1 << 12, - ContainsLexicalThis = 1 << 13, - ContainsCapturedLexicalThis = 1 << 14, - ContainsLexicalThisInComputedPropertyName = 1 << 15, - ContainsDefaultValueAssignments = 1 << 16, - ContainsParameterPropertyAssignments = 1 << 17, - ContainsSpreadElementExpression = 1 << 18, - ContainsComputedPropertyName = 1 << 19, - ContainsBlockScopedBinding = 1 << 20, - ContainsBindingPattern = 1 << 21, - ContainsYield = 1 << 22, - ContainsHoistedDeclarationOrCompletion = 1 << 23, + ContainsDecorators = 1 << 13, + ContainsPropertyInitializer = 1 << 14, + ContainsLexicalThis = 1 << 15, + ContainsCapturedLexicalThis = 1 << 16, + ContainsLexicalThisInComputedPropertyName = 1 << 17, + ContainsDefaultValueAssignments = 1 << 18, + ContainsParameterPropertyAssignments = 1 << 19, + ContainsSpreadElementExpression = 1 << 20, + ContainsComputedPropertyName = 1 << 21, + ContainsBlockScopedBinding = 1 << 22, + ContainsBindingPattern = 1 << 23, + ContainsYield = 1 << 24, + ContainsHoistedDeclarationOrCompletion = 1 << 25, HasComputedFlags = 1 << 29, // Transform flags have been computed. @@ -3126,6 +3153,7 @@ namespace ts { // - Bitmasks that are used to assert facts about the syntax of a node and its subtree. AssertTypeScript = TypeScript | ContainsTypeScript, AssertJsx = Jsx | ContainsJsx, + AssertExperimental = Experimental | ContainsExperimental, AssertES7 = ES7 | ContainsES7, AssertES6 = ES6 | ContainsES6, AssertGenerator = Generator | ContainsGenerator, @@ -3133,7 +3161,7 @@ namespace ts { // Scope Exclusions // - Bitmasks that exclude flags from propagating out of a specific context // into the subtree flags of their container. - NodeExcludes = TypeScript | Jsx | ES7 | ES6 | DestructuringAssignment | Generator | HasComputedFlags, + NodeExcludes = TypeScript | Jsx | Experimental | ES7 | ES6 | DestructuringAssignment | Generator | HasComputedFlags, ArrowFunctionExcludes = NodeExcludes | ContainsDecorators | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion, FunctionExcludes = NodeExcludes | ContainsDecorators | ContainsDefaultValueAssignments | ContainsCapturedLexicalThis | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion, ConstructorExcludes = NodeExcludes | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion, diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 7a22c5634d84f..67ea182bf9868 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3740,6 +3740,7 @@ namespace ts { const kind = node.kind; return kind === SyntaxKind.PropertyAssignment || kind === SyntaxKind.ShorthandPropertyAssignment + || kind === SyntaxKind.SpreadElement || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.GetAccessor || kind === SyntaxKind.SetAccessor diff --git a/src/harness/harness.ts b/src/harness/harness.ts index b4fd8736b2f7d..3d050b2cf2c6c 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1463,7 +1463,7 @@ namespace Harness { } if (typesError && symbolsError) { - throw new Error(typesError.message + ts.sys.newLine + symbolsError.message); + throw new Error(typesError.message + Harness.IO.newLine() + symbolsError.message); } if (typesError) { diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index dcda3e40b1a67..8766937c00851 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -458,7 +458,7 @@ namespace ts.FindAllReferences { return [localParentType.symbol]; } else if (localParentType.flags & TypeFlags.UnionOrIntersection) { - return getSymbolsForClassAndInterfaceComponents(localParentType); + return getSymbolsForClassAndInterfaceComponents(localParentType); } } } @@ -630,13 +630,13 @@ namespace ts.FindAllReferences { } } - function getSymbolsForClassAndInterfaceComponents(type: UnionOrIntersectionType, result: Symbol[] = []): Symbol[] { + function getSymbolsForClassAndInterfaceComponents(type: TypeOperatorType, result: Symbol[] = []): Symbol[] { for (const componentType of type.types) { if (componentType.symbol && componentType.symbol.getFlags() & (SymbolFlags.Class | SymbolFlags.Interface)) { result.push(componentType.symbol); } if (componentType.getFlags() & TypeFlags.UnionOrIntersection) { - getSymbolsForClassAndInterfaceComponents(componentType, result); + getSymbolsForClassAndInterfaceComponents(componentType, result); } } return result; From 7004652a0a714364eb627591df5e288220c58226 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 26 Sep 2016 09:31:34 -0700 Subject: [PATCH 002/224] Declaration emit spread types and downlevel spread --- src/compiler/declarationEmitter.ts | 10 +++ src/compiler/emitter.ts | 2 +- src/compiler/transformer.ts | 4 +- src/compiler/transformers/es7.ts | 3 +- src/compiler/transformers/experimental.ts | 80 +++++++++++++++++++++++ 5 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 src/compiler/transformers/experimental.ts diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index a015ad79b9791..58ed2211160bd 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -1121,6 +1121,14 @@ namespace ts { writeLine(); } + + function emitSpreadTypeElement(type: SpreadTypeElement) { + write("..."); + emitType(type.type); + write(";"); + writeLine(); + } + function emitVariableDeclaration(node: VariableDeclaration) { // If we are emitting property it isn't moduleElement and hence we already know it needs to be emitted // so there is no check needed to see if declaration is visible @@ -1702,6 +1710,8 @@ namespace ts { case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: return emitPropertyDeclaration(node); + case SyntaxKind.SpreadTypeElement: + return emitSpreadTypeElement(node as SpreadTypeElement); case SyntaxKind.EnumMember: return emitEnumMemberDeclaration(node); case SyntaxKind.ExportAssignment: diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 14e743cf59f76..cd00f7ad76905 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2195,7 +2195,7 @@ const _super = (function (geti, seti) { helpersEmitted = true; } - if (compilerOptions.jsx !== JsxEmit.Preserve && !assignEmitted && (node.flags & NodeFlags.HasJsxSpreadAttributes)) { + if (compilerOptions.jsx !== JsxEmit.Preserve && !assignEmitted && (node.flags & NodeFlags.HasSpreadAttribute)) { writeLines(assignHelper); assignEmitted = true; } diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 6242a2f23474c..2fa57730bf085 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -1,6 +1,7 @@ /// /// /// +/// /// /// /// @@ -176,6 +177,7 @@ namespace ts { transformers.push(transformJsx); } + transformers.push(transformExperimental); transformers.push(transformES7); if (languageVersion < ScriptTarget.ES6) { @@ -629,4 +631,4 @@ namespace ts { return t => t; } } -} \ No newline at end of file +} diff --git a/src/compiler/transformers/es7.ts b/src/compiler/transformers/es7.ts index 5d848bc608bad..5aaaacb9ecd7a 100644 --- a/src/compiler/transformers/es7.ts +++ b/src/compiler/transformers/es7.ts @@ -28,7 +28,6 @@ namespace ts { switch (node.kind) { case SyntaxKind.BinaryExpression: return visitBinaryExpression(node); - default: Debug.failBadSyntaxKind(node); return visitEachChild(node, visitor, context); @@ -94,4 +93,4 @@ namespace ts { } } } -} \ No newline at end of file +} diff --git a/src/compiler/transformers/experimental.ts b/src/compiler/transformers/experimental.ts new file mode 100644 index 0000000000000..715cf91afd3e6 --- /dev/null +++ b/src/compiler/transformers/experimental.ts @@ -0,0 +1,80 @@ +/// +/// + +/*@internal*/ +namespace ts { + export function transformExperimental(context: TransformationContext) { + return transformSourceFile; + + function transformSourceFile(node: SourceFile) { + return visitEachChild(node, visitor, context); + } + + function visitor(node: Node): VisitResult { + if (node.transformFlags & TransformFlags.Experimental) { + return visitorWorker(node); + } + else if (node.transformFlags & TransformFlags.ContainsExperimental) { + return visitEachChild(node, visitor, context); + } + else { + return node; + } + } + + function visitorWorker(node: Node): VisitResult { + switch (node.kind) { + case SyntaxKind.ObjectLiteralExpression: + return visitObjectLiteralExpression(node as ObjectLiteralExpression); + default: + Debug.failBadSyntaxKind(node); + return visitEachChild(node, visitor, context); + } + } + + function chunkObjectLiteralElements(elements: ObjectLiteralElement[]): Expression[] { + let chunkObject: (ShorthandPropertyAssignment | PropertyAssignment)[]; + const objects: Expression[] = []; + for (const e of elements) { + if (e.kind === SyntaxKind.SpreadElement) { + if (chunkObject) { + objects.push(createObjectLiteral(chunkObject)); + chunkObject = undefined; + } + const target = (e as SpreadElement).target; + objects.push(visitNode(target, visitor, isExpression)); + } + else { + if (!chunkObject) { + chunkObject = []; + } + if (e.kind === SyntaxKind.PropertyAssignment) { + const p = e as PropertyAssignment; + chunkObject.push(createPropertyAssignment(p.name, visitNode(p.initializer, visitor, isExpression))); + } + else { + chunkObject.push(e as ShorthandPropertyAssignment); + } + } + } + if (chunkObject) { + objects.push(createObjectLiteral(chunkObject)); + } + + return objects; + } + + function visitObjectLiteralExpression(node: ObjectLiteralExpression): Expression { + // spread elements emit like so: + // non-spread elements are chunked together into object literals, and then all are passed to __assign: + // { a, ...o, b } => __assign({a}, o, {b}); + // If the first element is a spread element, then the first argument to __assign is {}: + // { ...o, a, b, ...o2 } => __assign({}, o, {a, b}, o2) + const objects = chunkObjectLiteralElements(node.properties); + if (objects.length && objects[0].kind !== SyntaxKind.ObjectLiteralExpression) { + objects.unshift(createObjectLiteral()); + } + return createCall(createIdentifier("__assign"), undefined, objects); + } + } +} From bf866cea7b82f8c22d648acbe6815cfa9632d8db Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 26 Sep 2016 09:32:23 -0700 Subject: [PATCH 003/224] Update build files --- Jakefile.js | 2 ++ src/compiler/tsconfig.json | 1 + src/harness/tsconfig.json | 1 + src/services/tsconfig.json | 1 + 4 files changed, 5 insertions(+) diff --git a/Jakefile.js b/Jakefile.js index c62a4ffd6ce0e..6b749d4a4ef83 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -72,6 +72,7 @@ var compilerSources = [ "transformers/module/system.ts", "transformers/module/module.ts", "transformers/jsx.ts", + "transformers/experimental.ts", "transformers/es7.ts", "transformers/generators.ts", "transformers/es6.ts", @@ -106,6 +107,7 @@ var servicesSources = [ "transformers/module/system.ts", "transformers/module/module.ts", "transformers/jsx.ts", + "transformers/experimental.ts", "transformers/es7.ts", "transformers/generators.ts", "transformers/es6.ts", diff --git a/src/compiler/tsconfig.json b/src/compiler/tsconfig.json index f128c994af12b..9681aba0d8f1c 100644 --- a/src/compiler/tsconfig.json +++ b/src/compiler/tsconfig.json @@ -24,6 +24,7 @@ "visitor.ts", "transformers/ts.ts", "transformers/jsx.ts", + "transformers/experimental.ts", "transformers/es7.ts", "transformers/es6.ts", "transformers/generators.ts", diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json index 8444d1d4a751e..fda4c68272ef1 100644 --- a/src/harness/tsconfig.json +++ b/src/harness/tsconfig.json @@ -26,6 +26,7 @@ "../compiler/visitor.ts", "../compiler/transformers/ts.ts", "../compiler/transformers/jsx.ts", + "../compiler/transformers/experimental.ts", "../compiler/transformers/es7.ts", "../compiler/transformers/es6.ts", "../compiler/transformers/generators.ts", diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 58312c6f38ff3..8d612fb7a7557 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -25,6 +25,7 @@ "../compiler/visitor.ts", "../compiler/transformers/ts.ts", "../compiler/transformers/jsx.ts", + "../compiler/transformers/experimental.ts", "../compiler/transformers/es7.ts", "../compiler/transformers/es6.ts", "../compiler/transformers/generators.ts", From 0fea59f642604ecd105c424deae982a73b4032c7 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 26 Sep 2016 09:33:08 -0700 Subject: [PATCH 004/224] Add spread tests --- ...uringObjectBindingPatternAndAssignment3.ts | 5 +- .../es6/destructuring/interfaceSpread.ts | 8 ++ .../conformance/types/spread/objectSpread.ts | 96 +++++++++++++++++++ .../types/spread/objectSpreadGeneric.ts | 40 ++++++++ .../types/spread/objectSpreadNegative.ts | 49 ++++++++++ .../types/spread/objectSpreadNegativeParse.ts | 4 + .../completionListForObjectSpread.ts | 35 +++++++ .../fourslash/findAllRefsForObjectSpread.ts | 12 +++ .../fourslash/goToDefinitionObjectSpread.ts | 7 ++ tests/cases/fourslash/renameObjectSpread.ts | 18 ++++ 10 files changed, 270 insertions(+), 4 deletions(-) create mode 100644 tests/cases/conformance/es6/destructuring/interfaceSpread.ts create mode 100644 tests/cases/conformance/types/spread/objectSpread.ts create mode 100644 tests/cases/conformance/types/spread/objectSpreadGeneric.ts create mode 100644 tests/cases/conformance/types/spread/objectSpreadNegative.ts create mode 100644 tests/cases/conformance/types/spread/objectSpreadNegativeParse.ts create mode 100644 tests/cases/fourslash/completionListForObjectSpread.ts create mode 100644 tests/cases/fourslash/findAllRefsForObjectSpread.ts create mode 100644 tests/cases/fourslash/goToDefinitionObjectSpread.ts create mode 100644 tests/cases/fourslash/renameObjectSpread.ts diff --git a/tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment3.ts b/tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment3.ts index 2b3192bc9a2c3..1e2bc1e68b433 100644 --- a/tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment3.ts +++ b/tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment3.ts @@ -3,8 +3,5 @@ var {h?} = { h?: 1 }; var {i}: string | number = { i: 2 }; var {i1}: string | number| {} = { i1: 2 }; var { f2: {f21} = { f212: "string" } }: any = undefined; -var { ...d1 } = { - a: 1, b: 1, d1: 9, e: 10 -} var {1} = { 1 }; -var {"prop"} = { "prop": 1 }; \ No newline at end of file +var {"prop"} = { "prop": 1 }; diff --git a/tests/cases/conformance/es6/destructuring/interfaceSpread.ts b/tests/cases/conformance/es6/destructuring/interfaceSpread.ts new file mode 100644 index 0000000000000..a92a188e1a884 --- /dev/null +++ b/tests/cases/conformance/es6/destructuring/interfaceSpread.ts @@ -0,0 +1,8 @@ +interface Congealed { + ...T + ...U +} + +let sandwich: Congealed<{jam: number }, { peanutButter: number }>; +sandwich.jam; +sandwich.peanutButter; diff --git a/tests/cases/conformance/types/spread/objectSpread.ts b/tests/cases/conformance/types/spread/objectSpread.ts new file mode 100644 index 0000000000000..9828e86f0a6bc --- /dev/null +++ b/tests/cases/conformance/types/spread/objectSpread.ts @@ -0,0 +1,96 @@ +// @target: es5 +let o = { a: 1, b: 'no' } +let o2 = { b: 'yes', c: true } +let swap = { a: 'yes', b: -1 }; + +let addAfter: { a: number, b: string, c: boolean } = + { ...o, c: false } +let addBefore: { a: number, b: string, c: boolean } = + { c: false, ...o } +// Note: ignore still changes the order that properties are printed +let ignore: { a: number, b: string } = + { b: 'ignored', ...o } +let override: { a: number, b: string } = + { ...o, b: 'override' } +let nested: { a: number, b: boolean, c: string } = + { ...{ a: 3, ...{ b: false, c: 'overriden' } }, c: 'whatever' } +let combined: { a: number, b: string, c: boolean } = + { ...o, ...o2 } +let combinedBefore: { a: number, b: string, c: boolean } = + { b: 'ok', ...o, ...o2 } +let combinedMid: { a: number, b: string, c: boolean } = + { ...o, b: 'ok', ...o2 } +let combinedAfter: { a: number, b: string, c: boolean } = + { ...o, ...o2, b: 'ok' } +let combinedNested: { a: number, b: boolean, c: string, d: string } = + { ...{ a: 4, ...{ b: false, c: 'overriden' } }, d: 'actually new', ...{ a: 5, d: 'maybe new' } } +let combinedNestedChangeType: { a: number, b: boolean, c: number } = + { ...{ a: 1, ...{ b: false, c: 'overriden' } }, c: -1 } +let propertyNested: { a: { a: number, b: string } } = + { a: { ... o } } +// accessors don't copy the descriptor +// (which means that readonly getters become read/write properties) +let op = { get a () { return 6 } }; +let getter: { a: number, c: number } = + { ...op, c: 7 } +getter.a = 12; + +// null and undefined are just skipped +let spreadNull: { a: number } = + { a: 7, ...null } +let spreadUndefined: { a: number } = + { a: 7, ...undefined } + +// methods are not enumerable +class C { p = 1; m() { } } +let c: C = new C() +let spreadC: { p: number } = { ...c } + +// own methods are enumerable +let cplus: { p: number, plus(): void } = { ...c, plus() { return this.p + 1; } }; +cplus.plus(); + +// new field's type conflicting with existing field is OK +let changeTypeAfter: { a: string, b: string } = + { ...o, a: 'wrong type?' } +let changeTypeBefore: { a: number, b: string } = + { a: 'wrong type?', ...o }; +let changeTypeBoth: { a: string, b: number } = + { ...o, ...swap }; + +// optional +let definiteBoolean: { sn: boolean }; +let definiteString: { sn: string }; +let optionalString: { sn?: string }; +let optionalNumber: { sn?: number }; +let optionalUnionStops: { sn: string | number | boolean } = { ...definiteBoolean, ...definiteString, ...optionalNumber }; +let optionalUnionDuplicates: { sn: string | number } = { ...definiteBoolean, ...definiteString, ...optionalString, ...optionalNumber }; +let allOptional: { sn?: string | number } = { ...optionalString, ...optionalNumber }; + +// computed property +let computedFirst: { a: number, b: string, "before everything": number } = + { ['before everything']: 12, ...o, b: 'yes' } +let computedMiddle: { a: number, b: string, c: boolean, "in the middle": number } = + { ...o, ['in the middle']: 13, b: 'maybe?', ...o2 } +let computedAfter: { a: number, b: string, "at the end": number } = + { ...o, b: 'yeah', ['at the end']: 14 } +// shortcut syntax +let a = 12; +let shortCutted: { a: number, b: string } = { ...o, a } + +// generics +function f(t: T, u: U): { id: string, ...T, ...U } { + return { id: 'id', ...t, ...u }; +} +let exclusive: { id: string, a: number, b: string, c: string, d: boolean } = + f({ a: 1, b: 'yes' }, { c: 'no', d: false }) +let overlap: { id: string, a: number, b: string } = + f({ a: 1 }, { a: 2, b: 'extra' }) +let overlapConflict: { id:string, a: string } = + f({ a: 1 }, { a: 'mismatch' }) +let overwriteId: { id: boolean, a: number, c: number, d: string } = + f({ a: 1, id: true }, { c: 1, d: 'no' }) + +class D { m() { }; q = 2; } +let classesAreWrong: { id: string, ...C, ...D } = + f(new C(), new D()) diff --git a/tests/cases/conformance/types/spread/objectSpreadGeneric.ts b/tests/cases/conformance/types/spread/objectSpreadGeneric.ts new file mode 100644 index 0000000000000..552142004b70f --- /dev/null +++ b/tests/cases/conformance/types/spread/objectSpreadGeneric.ts @@ -0,0 +1,40 @@ +function f(t: T, u: U, v: V): void { + let o: { ...T, ...U, ...V }; + const same: { ...T, ...U, ...V } = o; // ok + const reversed: { ...V, ...U, ...T } = o; // error, reversed + const reversed2: { ...U, ...T, ...V } = o; // error, U and T are still reversed + const missingT: { ...U, ...V } = o; // error, missing T + const missingU: { ...T, ...V } = o; // error, missing U + const missingV: { ...T, ...U } = o; // error, missing V + const atEnd: { ...T, ...U, second: string } = { ...t, ...u, second: 'foo' }; // ok + const atBeginning: { first: string, ...T, ...U, } = { first: 'foo', ...t, ...u }; // ok + + const emptyTarget: { } = { ...t, ...u } // ok + const emptySource: { ...T, ...U } = { }; // error, {} is not assignable to U (or T) + + let optionalNumber: { sn?: number }; + let optionalString: { sn?: string }; + let optionalBoolean: { sn?: boolean }; + const unionCutoff: { ...T, sn?: number | string | boolean } = + { ...optionalBoolean, ...t, ...optionalString, ...optionalNumber } // ok + unionCutoff.sn; // ok + const optionalCutoff = { ...t, ...optionalNumber }; // ok + optionalCutoff.sn; // ok + + const interspersed: { first: string, ...T, second: string, ...U, third: string } = + { first: '1', ...t, second: '2', ...u, third: '3' }; // ok + const interspersedMissingU: { first: string, second: string, ...T, third: string } = + { first: '1', ...t, second: '2', ...u, third: '3' }; // error, 'U' is missing + const interspersedOrder1: { first: string, ...T, second: string, ...U, third: string, secondsecond: string } = + { first: '1', ...t, second: '2', ...u, third: '3', secondsecond: 'false' }; // ok + const interspersedOrder2: { first: string, second: string, secondsecond: string, third: string, ...T, ...U } = + { first: '1', ...t, second: '2', ...u, third: '3', secondsecond: 'false' }; // ok + + + const mismatchFirst: { first: string, ...T, second: string, ...U, third: string } = + { firrrrrrst: '1', ...t, second: '2', ...u, third: '3' }; // error, 'first' not found + const mismatchSecond: { first: string, ...T, second: string, ...U, third: string } = + { first: '1', ...t, ssssssssecond: '2', ...u, third: '3' }; // error, 'second' not found + const mismatchLast: { first: string, ...T, second: string, ...U, third: string } = + { first: '1', ...t, second: '2', ...u, thirrrrrrrd: '3' }; // error, 'third' not found +} diff --git a/tests/cases/conformance/types/spread/objectSpreadNegative.ts b/tests/cases/conformance/types/spread/objectSpreadNegative.ts new file mode 100644 index 0000000000000..4691e45a17df3 --- /dev/null +++ b/tests/cases/conformance/types/spread/objectSpreadNegative.ts @@ -0,0 +1,49 @@ +// @target: es5 +let o = { a: 1, b: 'no' } + +/// private propagates +class PrivateOptionalX { + private x?: number; +} +class PublicX { + public x: number; +} +let privateOptionalx: PrivateOptionalX; +let publicx: PublicX; +let o3 = { ...publicx, ...privateOptionalx }; +let sn: string | number = o3.x; // error, x is private +let optionalString: { sn?: string }; +let optionalNumber: { sn?: number }; +let allOptional: { sn: string | number } = { ...optionalString, ...optionalNumber }; +// error, 'sn' is optional in source, required in target + +// assignability as target +interface Bool { b: boolean }; +interface Str { s: string }; +let spread: { ...Bool, ...Str } = { s: 'foo' }; // error, missing 'b' +let b: Bool; +spread = b; // error, missing 's' + +// expressions are not allowed +let o1 = { ...1 + 1 }; +let o2 = { ...(1 + 1) }; + +// literal repeats are not allowed, but spread repeats are fine +let duplicated = { b: 'bad', ...o, b: 'bad', ...o2, b: 'bad' } +let duplicatedSpread = { ...o, ...o } + +// write-only properties get skipped +let setterOnly = { ...{ set b (bad: number) { } } }; +setterOnly.b = 12; // error, 'b' does not exist + +// methods are skipped because they aren't enumerable +class C { p = 1; m() { } } +let c: C = new C() +let spreadC = { ...c } +spreadC.m(); // error 'm' is not in '{ ... c }' + +let callableConstructableSpread: { ...PublicX, (n: number): number, new (p: number) }; +callableConstructableSpread(12); // error, no call signature +new callableConstructableSpread(12); // error, no construct signature + +let callableSpread = { ...publicx, ...(n => n + 1) }; // error, can't spread functions diff --git a/tests/cases/conformance/types/spread/objectSpreadNegativeParse.ts b/tests/cases/conformance/types/spread/objectSpreadNegativeParse.ts new file mode 100644 index 0000000000000..47fd787ec9a35 --- /dev/null +++ b/tests/cases/conformance/types/spread/objectSpreadNegativeParse.ts @@ -0,0 +1,4 @@ +let o7 = { ...o? }; +let o8 = { ...*o }; +let o9 = { ...matchMedia() { }}; +let o10 = { ...get x() { return 12; }}; diff --git a/tests/cases/fourslash/completionListForObjectSpread.ts b/tests/cases/fourslash/completionListForObjectSpread.ts new file mode 100644 index 0000000000000..ca96797195c15 --- /dev/null +++ b/tests/cases/fourslash/completionListForObjectSpread.ts @@ -0,0 +1,35 @@ +/// +////let o = { a: 1, b: 'no' } +////let o2 = { b: 'yes', c: true } +////let swap = { a: 'yes', b: -1 }; +////let addAfter: { a: number, b: string, c: boolean } = +//// { ...o, c: false } +////let addBefore: { a: number, b: string, c: boolean } = +//// { c: false, ...o } +////let ignore: { a: number, b: string } = +//// { b: 'ignored', ...o } +////ignore./*1*/a; +////let combinedNestedChangeType: { a: number, b: boolean, c: number } = +//// { ...{ a: 1, ...{ b: false, c: 'overriden' } }, c: -1 } +////combinedNestedChangeType./*2*/a; +////let spreadNull: { a: number } = +//// { a: 7, ...null } +////let spreadUndefined: { a: number } = +//// { a: 7, ...undefined } +////spreadNull./*3*/a; +////spreadUndefined./*4*/a; +goTo.marker('1'); +verify.memberListContains('a', '(property) a: number'); +verify.memberListContains('b', '(property) b: string'); +verify.memberListCount(2); +goTo.marker('2'); +verify.memberListContains('a', '(property) a: number'); +verify.memberListContains('b', '(property) b: boolean'); +verify.memberListContains('c', '(property) c: number'); +verify.memberListCount(3); +goTo.marker('3'); +verify.memberListContains('a', '(property) a: number'); +verify.memberListCount(1); +goTo.marker('4'); +verify.memberListContains('a', '(property) a: number'); +verify.memberListCount(1); diff --git a/tests/cases/fourslash/findAllRefsForObjectSpread.ts b/tests/cases/fourslash/findAllRefsForObjectSpread.ts new file mode 100644 index 0000000000000..fe0a7f9c20195 --- /dev/null +++ b/tests/cases/fourslash/findAllRefsForObjectSpread.ts @@ -0,0 +1,12 @@ +/// + +////interface A1 { [|a|]: number }; +////interface A2 { [|a|]?: number }; +////let a12: { ...A1, ...A2 }; +////a12.[|a|]; +const ranges = test.ranges(); +// members of spread types only refer to themselves and the resulting property +verify.referencesOf(ranges[0], [ranges[0], ranges[2]]); +verify.referencesOf(ranges[1], [ranges[1], ranges[2]]); +// but the resulting property refers to everything +verify.referencesOf(ranges[2], ranges); diff --git a/tests/cases/fourslash/goToDefinitionObjectSpread.ts b/tests/cases/fourslash/goToDefinitionObjectSpread.ts new file mode 100644 index 0000000000000..64623c36cb76b --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionObjectSpread.ts @@ -0,0 +1,7 @@ +/// + +////interface A1 { /*1*/a: number }; +////interface A2 { /*2*/a?: number }; +////let a12: { ...A1, ...A2 }; +////a12.a/*3*/; +verify.goToDefinition('3', [ '1', '2' ]); diff --git a/tests/cases/fourslash/renameObjectSpread.ts b/tests/cases/fourslash/renameObjectSpread.ts new file mode 100644 index 0000000000000..a2c640361e59c --- /dev/null +++ b/tests/cases/fourslash/renameObjectSpread.ts @@ -0,0 +1,18 @@ +/// + +////interface A1 { [|a|]: number }; +////interface A2 { [|a|]?: number }; +////let a12: { ...A1, ...A2 }; +////a12.[|a|]; +const ranges = test.ranges(); +verify.assertHasRanges(ranges); + +// A1 unions with A2, so rename A1.a and a12.a +goTo.position(ranges[0].start); +verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false, [ranges[0], ranges[2]]); +// A1 unions with A2, so rename A2.a and a12.a +goTo.position(ranges[1].start); +verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false, [ranges[1], ranges[2]]); +// a12.a unions A1.a and A2.a, so rename A1.a, A2.a and a12.a +goTo.position(ranges[2].start); +verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false, [ranges[0], ranges[1], ranges[2]]); From c8db211d90424abd949805264f724dc4adbbb53e Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 26 Sep 2016 09:33:27 -0700 Subject: [PATCH 005/224] Update baselines --- ...ectBindingPatternAndAssignment3.errors.txt | 26 +- ...uringObjectBindingPatternAndAssignment3.js | 9 +- .../duplicateObjectLiteralProperty.errors.txt | 8 +- .../reference/interfaceSpread.errors.txt | 21 + tests/baselines/reference/interfaceSpread.js | 15 + .../lastPropertyInLiteralWins.errors.txt | 8 +- .../reference/memberOverride.errors.txt | 5 +- .../reference/objectLiteralErrors.errors.txt | 5 +- ...tiesErrorFromNotUsingIdentifier.errors.txt | 8 +- tests/baselines/reference/objectSpread.js | 181 ++++++ .../baselines/reference/objectSpread.symbols | 369 ++++++++++++ tests/baselines/reference/objectSpread.types | 530 ++++++++++++++++++ .../reference/objectSpreadGeneric.errors.txt | 80 +++ .../reference/objectSpreadGeneric.js | 79 +++ .../reference/objectSpreadNegative.errors.txt | 93 +++ .../reference/objectSpreadNegative.js | 108 ++++ .../objectSpreadNegativeParse.errors.txt | 35 ++ .../reference/objectSpreadNegativeParse.js | 21 + 18 files changed, 1569 insertions(+), 32 deletions(-) create mode 100644 tests/baselines/reference/interfaceSpread.errors.txt create mode 100644 tests/baselines/reference/interfaceSpread.js create mode 100644 tests/baselines/reference/objectSpread.js create mode 100644 tests/baselines/reference/objectSpread.symbols create mode 100644 tests/baselines/reference/objectSpread.types create mode 100644 tests/baselines/reference/objectSpreadGeneric.errors.txt create mode 100644 tests/baselines/reference/objectSpreadGeneric.js create mode 100644 tests/baselines/reference/objectSpreadNegative.errors.txt create mode 100644 tests/baselines/reference/objectSpreadNegative.js create mode 100644 tests/baselines/reference/objectSpreadNegativeParse.errors.txt create mode 100644 tests/baselines/reference/objectSpreadNegativeParse.js diff --git a/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment3.errors.txt b/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment3.errors.txt index bb4b79987e349..33344b9b5eb00 100644 --- a/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment3.errors.txt +++ b/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment3.errors.txt @@ -5,16 +5,12 @@ tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAs tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment3.ts(4,6): error TS2459: Type 'string | number | {}' has no property 'i1' and no string index signature. tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment3.ts(5,12): error TS2525: Initializer provides no value for this binding element and the binding element has no default value. tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment3.ts(5,21): error TS2353: Object literal may only specify known properties, and 'f212' does not exist in type '{ f21: any; }'. -tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment3.ts(6,7): error TS1180: Property destructuring pattern expected. -tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment3.ts(7,5): error TS2353: Object literal may only specify known properties, and 'a' does not exist in type '{ d1: any; }'. -tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment3.ts(7,11): error TS2353: Object literal may only specify known properties, and 'b' does not exist in type '{ d1: any; }'. -tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment3.ts(7,24): error TS2353: Object literal may only specify known properties, and 'e' does not exist in type '{ d1: any; }'. -tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment3.ts(9,7): error TS1005: ':' expected. -tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment3.ts(9,15): error TS1005: ':' expected. -tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment3.ts(10,12): error TS1005: ':' expected. +tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment3.ts(6,7): error TS1005: ':' expected. +tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment3.ts(6,15): error TS1005: ':' expected. +tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment3.ts(7,12): error TS1005: ':' expected. -==== tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment3.ts (13 errors) ==== +==== tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment3.ts (9 errors) ==== // Error var {h?} = { h?: 1 }; ~ @@ -33,17 +29,6 @@ tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAs !!! error TS2525: Initializer provides no value for this binding element and the binding element has no default value. ~~~~ !!! error TS2353: Object literal may only specify known properties, and 'f212' does not exist in type '{ f21: any; }'. - var { ...d1 } = { - ~~~ -!!! error TS1180: Property destructuring pattern expected. - a: 1, b: 1, d1: 9, e: 10 - ~ -!!! error TS2353: Object literal may only specify known properties, and 'a' does not exist in type '{ d1: any; }'. - ~ -!!! error TS2353: Object literal may only specify known properties, and 'b' does not exist in type '{ d1: any; }'. - ~ -!!! error TS2353: Object literal may only specify known properties, and 'e' does not exist in type '{ d1: any; }'. - } var {1} = { 1 }; ~ !!! error TS1005: ':' expected. @@ -51,4 +36,5 @@ tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAs !!! error TS1005: ':' expected. var {"prop"} = { "prop": 1 }; ~ -!!! error TS1005: ':' expected. \ No newline at end of file +!!! error TS1005: ':' expected. + \ No newline at end of file diff --git a/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment3.js b/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment3.js index 77288e6a5427a..0872a71c73bd8 100644 --- a/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment3.js +++ b/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment3.js @@ -4,11 +4,9 @@ var {h?} = { h?: 1 }; var {i}: string | number = { i: 2 }; var {i1}: string | number| {} = { i1: 2 }; var { f2: {f21} = { f212: "string" } }: any = undefined; -var { ...d1 } = { - a: 1, b: 1, d1: 9, e: 10 -} var {1} = { 1 }; -var {"prop"} = { "prop": 1 }; +var {"prop"} = { "prop": 1 }; + //// [destructuringObjectBindingPatternAndAssignment3.js] // Error @@ -16,8 +14,5 @@ var h = { h: 1 }.h; var i = { i: 2 }.i; var i1 = { i1: 2 }.i1; var _a = undefined.f2, f21 = (_a === void 0 ? { f212: "string" } : _a).f21; -var d1 = { - a: 1, b: 1, d1: 9, e: 10 -}.d1; var = { 1: }[1]; var = { "prop": 1 }["prop"]; diff --git a/tests/baselines/reference/duplicateObjectLiteralProperty.errors.txt b/tests/baselines/reference/duplicateObjectLiteralProperty.errors.txt index abc56a6f8fce7..926a707122293 100644 --- a/tests/baselines/reference/duplicateObjectLiteralProperty.errors.txt +++ b/tests/baselines/reference/duplicateObjectLiteralProperty.errors.txt @@ -1,6 +1,8 @@ tests/cases/compiler/duplicateObjectLiteralProperty.ts(4,5): error TS2300: Duplicate identifier 'a'. tests/cases/compiler/duplicateObjectLiteralProperty.ts(5,5): error TS2300: Duplicate identifier '\u0061'. +tests/cases/compiler/duplicateObjectLiteralProperty.ts(5,5): error TS2698: Cannot change type of property 'a' from 'number' to 'string'. tests/cases/compiler/duplicateObjectLiteralProperty.ts(6,5): error TS2300: Duplicate identifier 'a'. +tests/cases/compiler/duplicateObjectLiteralProperty.ts(6,5): error TS2698: Cannot change type of property 'a' from 'string' to '{ c: number; }'. tests/cases/compiler/duplicateObjectLiteralProperty.ts(8,9): error TS2300: Duplicate identifier '"c"'. tests/cases/compiler/duplicateObjectLiteralProperty.ts(14,9): error TS2300: Duplicate identifier 'a'. tests/cases/compiler/duplicateObjectLiteralProperty.ts(15,9): error TS2300: Duplicate identifier 'a'. @@ -8,7 +10,7 @@ tests/cases/compiler/duplicateObjectLiteralProperty.ts(16,9): error TS1118: An o tests/cases/compiler/duplicateObjectLiteralProperty.ts(16,9): error TS2300: Duplicate identifier 'a'. -==== tests/cases/compiler/duplicateObjectLiteralProperty.ts (8 errors) ==== +==== tests/cases/compiler/duplicateObjectLiteralProperty.ts (10 errors) ==== var x = { a: 1, b: true, // OK @@ -18,9 +20,13 @@ tests/cases/compiler/duplicateObjectLiteralProperty.ts(16,9): error TS2300: Dupl \u0061: "ss", // Duplicate ~~~~~~ !!! error TS2300: Duplicate identifier '\u0061'. + ~~~~~~ +!!! error TS2698: Cannot change type of property 'a' from 'number' to 'string'. a: { ~ !!! error TS2300: Duplicate identifier 'a'. + ~ +!!! error TS2698: Cannot change type of property 'a' from 'string' to '{ c: number; }'. c: 1, "c": 56, // Duplicate ~~~ diff --git a/tests/baselines/reference/interfaceSpread.errors.txt b/tests/baselines/reference/interfaceSpread.errors.txt new file mode 100644 index 0000000000000..27aab11491c5a --- /dev/null +++ b/tests/baselines/reference/interfaceSpread.errors.txt @@ -0,0 +1,21 @@ +tests/cases/conformance/es6/destructuring/interfaceSpread.ts(2,5): error TS2699: Interface declaration cannot contain a spread property. +tests/cases/conformance/es6/destructuring/interfaceSpread.ts(7,10): error TS2339: Property 'jam' does not exist on type 'Congealed<{ jam: number; }, { peanutButter: number; }>'. +tests/cases/conformance/es6/destructuring/interfaceSpread.ts(8,10): error TS2339: Property 'peanutButter' does not exist on type 'Congealed<{ jam: number; }, { peanutButter: number; }>'. + + +==== tests/cases/conformance/es6/destructuring/interfaceSpread.ts (3 errors) ==== + interface Congealed { + ...T + ~~~~ +!!! error TS2699: Interface declaration cannot contain a spread property. + ...U + } + + let sandwich: Congealed<{jam: number }, { peanutButter: number }>; + sandwich.jam; + ~~~ +!!! error TS2339: Property 'jam' does not exist on type 'Congealed<{ jam: number; }, { peanutButter: number; }>'. + sandwich.peanutButter; + ~~~~~~~~~~~~ +!!! error TS2339: Property 'peanutButter' does not exist on type 'Congealed<{ jam: number; }, { peanutButter: number; }>'. + \ No newline at end of file diff --git a/tests/baselines/reference/interfaceSpread.js b/tests/baselines/reference/interfaceSpread.js new file mode 100644 index 0000000000000..89f2531436667 --- /dev/null +++ b/tests/baselines/reference/interfaceSpread.js @@ -0,0 +1,15 @@ +//// [interfaceSpread.ts] +interface Congealed { + ...T + ...U +} + +let sandwich: Congealed<{jam: number }, { peanutButter: number }>; +sandwich.jam; +sandwich.peanutButter; + + +//// [interfaceSpread.js] +var sandwich; +sandwich.jam; +sandwich.peanutButter; diff --git a/tests/baselines/reference/lastPropertyInLiteralWins.errors.txt b/tests/baselines/reference/lastPropertyInLiteralWins.errors.txt index 2b4a694975b4c..c8844f2c5721f 100644 --- a/tests/baselines/reference/lastPropertyInLiteralWins.errors.txt +++ b/tests/baselines/reference/lastPropertyInLiteralWins.errors.txt @@ -4,10 +4,12 @@ tests/cases/compiler/lastPropertyInLiteralWins.ts(7,6): error TS2345: Argument o Types of parameters 'num' and 'str' are incompatible. Type 'string' is not assignable to type 'number'. tests/cases/compiler/lastPropertyInLiteralWins.ts(9,5): error TS2300: Duplicate identifier 'thunk'. +tests/cases/compiler/lastPropertyInLiteralWins.ts(9,5): error TS2698: Cannot change type of property 'thunk' from '(str: string) => void' to '(num: number) => void'. tests/cases/compiler/lastPropertyInLiteralWins.ts(14,5): error TS2300: Duplicate identifier 'thunk'. +tests/cases/compiler/lastPropertyInLiteralWins.ts(14,5): error TS2698: Cannot change type of property 'thunk' from '(num: number) => void' to '(str: string) => void'. -==== tests/cases/compiler/lastPropertyInLiteralWins.ts (3 errors) ==== +==== tests/cases/compiler/lastPropertyInLiteralWins.ts (5 errors) ==== interface Thing { thunk: (str: string) => void; } @@ -22,6 +24,8 @@ tests/cases/compiler/lastPropertyInLiteralWins.ts(14,5): error TS2300: Duplicate ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~ !!! error TS2300: Duplicate identifier 'thunk'. + ~~~~~ +!!! error TS2698: Cannot change type of property 'thunk' from '(str: string) => void' to '(num: number) => void'. }); ~ !!! error TS2345: Argument of type '{ thunk: (num: number) => void; }' is not assignable to parameter of type 'Thing'. @@ -35,5 +39,7 @@ tests/cases/compiler/lastPropertyInLiteralWins.ts(14,5): error TS2300: Duplicate thunk: (str: string) => {} ~~~~~ !!! error TS2300: Duplicate identifier 'thunk'. + ~~~~~ +!!! error TS2698: Cannot change type of property 'thunk' from '(num: number) => void' to '(str: string) => void'. }); \ No newline at end of file diff --git a/tests/baselines/reference/memberOverride.errors.txt b/tests/baselines/reference/memberOverride.errors.txt index 15c4b2ad37bb8..355db0015057f 100644 --- a/tests/baselines/reference/memberOverride.errors.txt +++ b/tests/baselines/reference/memberOverride.errors.txt @@ -1,7 +1,8 @@ tests/cases/compiler/memberOverride.ts(5,5): error TS2300: Duplicate identifier 'a'. +tests/cases/compiler/memberOverride.ts(5,5): error TS2698: Cannot change type of property 'a' from 'string' to 'number'. -==== tests/cases/compiler/memberOverride.ts (1 errors) ==== +==== tests/cases/compiler/memberOverride.ts (2 errors) ==== // An object initialiser accepts the first definition for the same property with a different type signature // Should compile, since the second declaration of a overrides the first var x = { @@ -9,6 +10,8 @@ tests/cases/compiler/memberOverride.ts(5,5): error TS2300: Duplicate identifier a: 5 ~ !!! error TS2300: Duplicate identifier 'a'. + ~ +!!! error TS2698: Cannot change type of property 'a' from 'string' to 'number'. } var n: number = x.a; \ No newline at end of file diff --git a/tests/baselines/reference/objectLiteralErrors.errors.txt b/tests/baselines/reference/objectLiteralErrors.errors.txt index 234ac24a8fdc5..f70f255df05b8 100644 --- a/tests/baselines/reference/objectLiteralErrors.errors.txt +++ b/tests/baselines/reference/objectLiteralErrors.errors.txt @@ -1,6 +1,7 @@ tests/cases/conformance/expressions/objectLiterals/objectLiteralErrors.ts(3,18): error TS2300: Duplicate identifier 'a'. tests/cases/conformance/expressions/objectLiterals/objectLiteralErrors.ts(4,19): error TS2300: Duplicate identifier 'a'. tests/cases/conformance/expressions/objectLiterals/objectLiteralErrors.ts(5,18): error TS2300: Duplicate identifier 'a'. +tests/cases/conformance/expressions/objectLiterals/objectLiteralErrors.ts(5,18): error TS2698: Cannot change type of property 'a' from 'number' to 'string'. tests/cases/conformance/expressions/objectLiterals/objectLiteralErrors.ts(6,21): error TS2300: Duplicate identifier 'a'. tests/cases/conformance/expressions/objectLiterals/objectLiteralErrors.ts(7,19): error TS2300: Duplicate identifier 'a'. tests/cases/conformance/expressions/objectLiterals/objectLiteralErrors.ts(8,18): error TS2300: Duplicate identifier ''a''. @@ -78,7 +79,7 @@ tests/cases/conformance/expressions/objectLiterals/objectLiteralErrors.ts(45,16) tests/cases/conformance/expressions/objectLiterals/objectLiteralErrors.ts(45,55): error TS2380: 'get' and 'set' accessor must have the same type. -==== tests/cases/conformance/expressions/objectLiterals/objectLiteralErrors.ts (78 errors) ==== +==== tests/cases/conformance/expressions/objectLiterals/objectLiteralErrors.ts (79 errors) ==== // Multiple properties with the same name var e1 = { a: 0, a: 0 }; @@ -90,6 +91,8 @@ tests/cases/conformance/expressions/objectLiterals/objectLiteralErrors.ts(45,55) var e3 = { a: 0, a: '' }; ~ !!! error TS2300: Duplicate identifier 'a'. + ~ +!!! error TS2698: Cannot change type of property 'a' from 'number' to 'string'. var e4 = { a: true, a: false }; ~ !!! error TS2300: Duplicate identifier 'a'. diff --git a/tests/baselines/reference/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.errors.txt b/tests/baselines/reference/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.errors.txt index 5fa8a12e87559..cbe19cab2d25b 100644 --- a/tests/baselines/reference/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.errors.txt +++ b/tests/baselines/reference/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.errors.txt @@ -8,12 +8,14 @@ tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPr tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.ts(10,10): error TS1005: ':' expected. tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.ts(12,1): error TS1005: ':' expected. tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.ts(15,6): error TS1005: ':' expected. +tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.ts(16,5): error TS2698: Cannot change type of property 'a' from 'any' to 'string[]'. tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.ts(16,6): error TS1005: ':' expected. +tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.ts(17,5): error TS2698: Cannot change type of property 'a' from 'string[]' to 'number[]'. tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.ts(17,6): error TS1005: ':' expected. tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.ts(20,17): error TS1005: ':' expected. -==== tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.ts (13 errors) ==== +==== tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.ts (15 errors) ==== // errors var y = { "stringLiteral", @@ -50,9 +52,13 @@ tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPr ~ !!! error TS1005: ':' expected. a["ss"], + ~ +!!! error TS2698: Cannot change type of property 'a' from 'any' to 'string[]'. ~ !!! error TS1005: ':' expected. a[1], + ~ +!!! error TS2698: Cannot change type of property 'a' from 'string[]' to 'number[]'. ~ !!! error TS1005: ':' expected. }; diff --git a/tests/baselines/reference/objectSpread.js b/tests/baselines/reference/objectSpread.js new file mode 100644 index 0000000000000..a59577fe9dece --- /dev/null +++ b/tests/baselines/reference/objectSpread.js @@ -0,0 +1,181 @@ +//// [objectSpread.ts] +let o = { a: 1, b: 'no' } +let o2 = { b: 'yes', c: true } +let swap = { a: 'yes', b: -1 }; + +let addAfter: { a: number, b: string, c: boolean } = + { ...o, c: false } +let addBefore: { a: number, b: string, c: boolean } = + { c: false, ...o } +// Note: ignore still changes the order that properties are printed +let ignore: { a: number, b: string } = + { b: 'ignored', ...o } +let override: { a: number, b: string } = + { ...o, b: 'override' } +let nested: { a: number, b: boolean, c: string } = + { ...{ a: 3, ...{ b: false, c: 'overriden' } }, c: 'whatever' } +let combined: { a: number, b: string, c: boolean } = + { ...o, ...o2 } +let combinedBefore: { a: number, b: string, c: boolean } = + { b: 'ok', ...o, ...o2 } +let combinedMid: { a: number, b: string, c: boolean } = + { ...o, b: 'ok', ...o2 } +let combinedAfter: { a: number, b: string, c: boolean } = + { ...o, ...o2, b: 'ok' } +let combinedNested: { a: number, b: boolean, c: string, d: string } = + { ...{ a: 4, ...{ b: false, c: 'overriden' } }, d: 'actually new', ...{ a: 5, d: 'maybe new' } } +let combinedNestedChangeType: { a: number, b: boolean, c: number } = + { ...{ a: 1, ...{ b: false, c: 'overriden' } }, c: -1 } +let propertyNested: { a: { a: number, b: string } } = + { a: { ... o } } +// accessors don't copy the descriptor +// (which means that readonly getters become read/write properties) +let op = { get a () { return 6 } }; +let getter: { a: number, c: number } = + { ...op, c: 7 } +getter.a = 12; + +// null and undefined are just skipped +let spreadNull: { a: number } = + { a: 7, ...null } +let spreadUndefined: { a: number } = + { a: 7, ...undefined } + +// methods are not enumerable +class C { p = 1; m() { } } +let c: C = new C() +let spreadC: { p: number } = { ...c } + +// own methods are enumerable +let cplus: { p: number, plus(): void } = { ...c, plus() { return this.p + 1; } }; +cplus.plus(); + +// new field's type conflicting with existing field is OK +let changeTypeAfter: { a: string, b: string } = + { ...o, a: 'wrong type?' } +let changeTypeBefore: { a: number, b: string } = + { a: 'wrong type?', ...o }; +let changeTypeBoth: { a: string, b: number } = + { ...o, ...swap }; + +// optional +let definiteBoolean: { sn: boolean }; +let definiteString: { sn: string }; +let optionalString: { sn?: string }; +let optionalNumber: { sn?: number }; +let optionalUnionStops: { sn: string | number | boolean } = { ...definiteBoolean, ...definiteString, ...optionalNumber }; +let optionalUnionDuplicates: { sn: string | number } = { ...definiteBoolean, ...definiteString, ...optionalString, ...optionalNumber }; +let allOptional: { sn?: string | number } = { ...optionalString, ...optionalNumber }; + +// computed property +let computedFirst: { a: number, b: string, "before everything": number } = + { ['before everything']: 12, ...o, b: 'yes' } +let computedMiddle: { a: number, b: string, c: boolean, "in the middle": number } = + { ...o, ['in the middle']: 13, b: 'maybe?', ...o2 } +let computedAfter: { a: number, b: string, "at the end": number } = + { ...o, b: 'yeah', ['at the end']: 14 } +// shortcut syntax +let a = 12; +let shortCutted: { a: number, b: string } = { ...o, a } + +// generics +function f(t: T, u: U): { id: string, ...T, ...U } { + return { id: 'id', ...t, ...u }; +} +let exclusive: { id: string, a: number, b: string, c: string, d: boolean } = + f({ a: 1, b: 'yes' }, { c: 'no', d: false }) +let overlap: { id: string, a: number, b: string } = + f({ a: 1 }, { a: 2, b: 'extra' }) +let overlapConflict: { id:string, a: string } = + f({ a: 1 }, { a: 'mismatch' }) +let overwriteId: { id: boolean, a: number, c: number, d: string } = + f({ a: 1, id: true }, { c: 1, d: 'no' }) + +class D { m() { }; q = 2; } +let classesAreWrong: { id: string, ...C, ...D } = + f(new C(), new D()) + + +//// [objectSpread.js] +var __assign = (this && this.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; +}; +var o = { a: 1, b: 'no' }; +var o2 = { b: 'yes', c: true }; +var swap = { a: 'yes', b: -1 }; +var addAfter = __assign({}, o, { c: false }); +var addBefore = __assign({ c: false }, o); +// Note: ignore still changes the order that properties are printed +var ignore = __assign({ b: 'ignored' }, o); +var override = __assign({}, o, { b: 'override' }); +var nested = __assign({}, __assign({ a: 3 }, { b: false, c: 'overriden' }), { c: 'whatever' }); +var combined = __assign({}, o, o2); +var combinedBefore = __assign({ b: 'ok' }, o, o2); +var combinedMid = __assign({}, o, { b: 'ok' }, o2); +var combinedAfter = __assign({}, o, o2, { b: 'ok' }); +var combinedNested = __assign({}, __assign({ a: 4 }, { b: false, c: 'overriden' }), { d: 'actually new' }, { a: 5, d: 'maybe new' }); +var combinedNestedChangeType = __assign({}, __assign({ a: 1 }, { b: false, c: 'overriden' }), { c: -1 }); +var propertyNested = __assign({ a: __assign({}, o) }); +// accessors don't copy the descriptor +// (which means that readonly getters become read/write properties) +var op = { get a() { return 6; } }; +var getter = __assign({}, op, { c: 7 }); +getter.a = 12; +// null and undefined are just skipped +var spreadNull = __assign({ a: 7 }, null); +var spreadUndefined = __assign({ a: 7 }, undefined); +// methods are not enumerable +var C = (function () { + function C() { + this.p = 1; + } + C.prototype.m = function () { }; + return C; +}()); +var c = new C(); +var spreadC = __assign({}, c); +// own methods are enumerable +var cplus = __assign({}, c, { plus: function () { return this.p + 1; } }); +cplus.plus(); +// new field's type conflicting with existing field is OK +var changeTypeAfter = __assign({}, o, { a: 'wrong type?' }); +var changeTypeBefore = __assign({ a: 'wrong type?' }, o); +var changeTypeBoth = __assign({}, o, swap); +// optional +var definiteBoolean; +var definiteString; +var optionalString; +var optionalNumber; +var optionalUnionStops = __assign({}, definiteBoolean, definiteString, optionalNumber); +var optionalUnionDuplicates = __assign({}, definiteBoolean, definiteString, optionalString, optionalNumber); +var allOptional = __assign({}, optionalString, optionalNumber); +// computed property +var computedFirst = __assign((_a = {}, _a['before everything'] = 12, _a), o, { b: 'yes' }); +var computedMiddle = __assign({}, o, (_b = {}, _b['in the middle'] = 13, _b.b = 'maybe?', _b), o2); +var computedAfter = __assign({}, o, (_c = { b: 'yeah' }, _c['at the end'] = 14, _c)); +// shortcut syntax +var a = 12; +var shortCutted = __assign({}, o, { a: a }); +// generics +function f(t, u) { + return __assign({ id: 'id' }, t, u); +} +var exclusive = f({ a: 1, b: 'yes' }, { c: 'no', d: false }); +var overlap = f({ a: 1 }, { a: 2, b: 'extra' }); +var overlapConflict = f({ a: 1 }, { a: 'mismatch' }); +var overwriteId = f({ a: 1, id: true }, { c: 1, d: 'no' }); +var D = (function () { + function D() { + this.q = 2; + } + D.prototype.m = function () { }; + ; + return D; +}()); +var classesAreWrong = f(new C(), new D()); +var _a, _b, _c; diff --git a/tests/baselines/reference/objectSpread.symbols b/tests/baselines/reference/objectSpread.symbols new file mode 100644 index 0000000000000..001c42804d2b4 --- /dev/null +++ b/tests/baselines/reference/objectSpread.symbols @@ -0,0 +1,369 @@ +=== tests/cases/conformance/types/spread/objectSpread.ts === +let o = { a: 1, b: 'no' } +>o : Symbol(o, Decl(objectSpread.ts, 0, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 0, 9)) +>b : Symbol(b, Decl(objectSpread.ts, 0, 15)) + +let o2 = { b: 'yes', c: true } +>o2 : Symbol(o2, Decl(objectSpread.ts, 1, 3)) +>b : Symbol(b, Decl(objectSpread.ts, 1, 10)) +>c : Symbol(c, Decl(objectSpread.ts, 1, 20)) + +let swap = { a: 'yes', b: -1 }; +>swap : Symbol(swap, Decl(objectSpread.ts, 2, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 2, 12)) +>b : Symbol(b, Decl(objectSpread.ts, 2, 22)) + +let addAfter: { a: number, b: string, c: boolean } = +>addAfter : Symbol(addAfter, Decl(objectSpread.ts, 4, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 4, 15)) +>b : Symbol(b, Decl(objectSpread.ts, 4, 26)) +>c : Symbol(c, Decl(objectSpread.ts, 4, 37)) + + { ...o, c: false } +>c : Symbol(c, Decl(objectSpread.ts, 5, 11)) + +let addBefore: { a: number, b: string, c: boolean } = +>addBefore : Symbol(addBefore, Decl(objectSpread.ts, 6, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 6, 16)) +>b : Symbol(b, Decl(objectSpread.ts, 6, 27)) +>c : Symbol(c, Decl(objectSpread.ts, 6, 38)) + + { c: false, ...o } +>c : Symbol(c, Decl(objectSpread.ts, 7, 5)) + +// Note: ignore still changes the order that properties are printed +let ignore: { a: number, b: string } = +>ignore : Symbol(ignore, Decl(objectSpread.ts, 9, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 9, 13)) +>b : Symbol(b, Decl(objectSpread.ts, 9, 24)) + + { b: 'ignored', ...o } +>b : Symbol(b, Decl(objectSpread.ts, 10, 5)) + +let override: { a: number, b: string } = +>override : Symbol(override, Decl(objectSpread.ts, 11, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 11, 15)) +>b : Symbol(b, Decl(objectSpread.ts, 11, 26)) + + { ...o, b: 'override' } +>b : Symbol(b, Decl(objectSpread.ts, 12, 11)) + +let nested: { a: number, b: boolean, c: string } = +>nested : Symbol(nested, Decl(objectSpread.ts, 13, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 13, 13)) +>b : Symbol(b, Decl(objectSpread.ts, 13, 24)) +>c : Symbol(c, Decl(objectSpread.ts, 13, 36)) + + { ...{ a: 3, ...{ b: false, c: 'overriden' } }, c: 'whatever' } +>a : Symbol(a, Decl(objectSpread.ts, 14, 10)) +>b : Symbol(b, Decl(objectSpread.ts, 14, 21)) +>c : Symbol(c, Decl(objectSpread.ts, 14, 31)) +>c : Symbol(c, Decl(objectSpread.ts, 14, 51)) + +let combined: { a: number, b: string, c: boolean } = +>combined : Symbol(combined, Decl(objectSpread.ts, 15, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 15, 15)) +>b : Symbol(b, Decl(objectSpread.ts, 15, 26)) +>c : Symbol(c, Decl(objectSpread.ts, 15, 37)) + + { ...o, ...o2 } +let combinedBefore: { a: number, b: string, c: boolean } = +>combinedBefore : Symbol(combinedBefore, Decl(objectSpread.ts, 17, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 17, 21)) +>b : Symbol(b, Decl(objectSpread.ts, 17, 32)) +>c : Symbol(c, Decl(objectSpread.ts, 17, 43)) + + { b: 'ok', ...o, ...o2 } +>b : Symbol(b, Decl(objectSpread.ts, 18, 5)) + +let combinedMid: { a: number, b: string, c: boolean } = +>combinedMid : Symbol(combinedMid, Decl(objectSpread.ts, 19, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 19, 18)) +>b : Symbol(b, Decl(objectSpread.ts, 19, 29)) +>c : Symbol(c, Decl(objectSpread.ts, 19, 40)) + + { ...o, b: 'ok', ...o2 } +>b : Symbol(b, Decl(objectSpread.ts, 20, 11)) + +let combinedAfter: { a: number, b: string, c: boolean } = +>combinedAfter : Symbol(combinedAfter, Decl(objectSpread.ts, 21, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 21, 20)) +>b : Symbol(b, Decl(objectSpread.ts, 21, 31)) +>c : Symbol(c, Decl(objectSpread.ts, 21, 42)) + + { ...o, ...o2, b: 'ok' } +>b : Symbol(b, Decl(objectSpread.ts, 22, 18)) + +let combinedNested: { a: number, b: boolean, c: string, d: string } = +>combinedNested : Symbol(combinedNested, Decl(objectSpread.ts, 23, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 23, 21)) +>b : Symbol(b, Decl(objectSpread.ts, 23, 32)) +>c : Symbol(c, Decl(objectSpread.ts, 23, 44)) +>d : Symbol(d, Decl(objectSpread.ts, 23, 55)) + + { ...{ a: 4, ...{ b: false, c: 'overriden' } }, d: 'actually new', ...{ a: 5, d: 'maybe new' } } +>a : Symbol(a, Decl(objectSpread.ts, 24, 10)) +>b : Symbol(b, Decl(objectSpread.ts, 24, 21)) +>c : Symbol(c, Decl(objectSpread.ts, 24, 31)) +>d : Symbol(d, Decl(objectSpread.ts, 24, 51)) +>a : Symbol(a, Decl(objectSpread.ts, 24, 75)) +>d : Symbol(d, Decl(objectSpread.ts, 24, 81)) + +let combinedNestedChangeType: { a: number, b: boolean, c: number } = +>combinedNestedChangeType : Symbol(combinedNestedChangeType, Decl(objectSpread.ts, 25, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 25, 31)) +>b : Symbol(b, Decl(objectSpread.ts, 25, 42)) +>c : Symbol(c, Decl(objectSpread.ts, 25, 54)) + + { ...{ a: 1, ...{ b: false, c: 'overriden' } }, c: -1 } +>a : Symbol(a, Decl(objectSpread.ts, 26, 10)) +>b : Symbol(b, Decl(objectSpread.ts, 26, 21)) +>c : Symbol(c, Decl(objectSpread.ts, 26, 31)) +>c : Symbol(c, Decl(objectSpread.ts, 26, 51)) + +let propertyNested: { a: { a: number, b: string } } = +>propertyNested : Symbol(propertyNested, Decl(objectSpread.ts, 27, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 27, 21)) +>a : Symbol(a, Decl(objectSpread.ts, 27, 26)) +>b : Symbol(b, Decl(objectSpread.ts, 27, 37)) + + { a: { ... o } } +>a : Symbol(a, Decl(objectSpread.ts, 28, 5)) + +// accessors don't copy the descriptor +// (which means that readonly getters become read/write properties) +let op = { get a () { return 6 } }; +>op : Symbol(op, Decl(objectSpread.ts, 31, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 31, 10)) + +let getter: { a: number, c: number } = +>getter : Symbol(getter, Decl(objectSpread.ts, 32, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 32, 13)) +>c : Symbol(c, Decl(objectSpread.ts, 32, 24)) + + { ...op, c: 7 } +>c : Symbol(c, Decl(objectSpread.ts, 33, 12)) + +getter.a = 12; +>getter.a : Symbol(a, Decl(objectSpread.ts, 32, 13)) +>getter : Symbol(getter, Decl(objectSpread.ts, 32, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 32, 13)) + +// null and undefined are just skipped +let spreadNull: { a: number } = +>spreadNull : Symbol(spreadNull, Decl(objectSpread.ts, 37, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 37, 17)) + + { a: 7, ...null } +>a : Symbol(a, Decl(objectSpread.ts, 38, 5)) + +let spreadUndefined: { a: number } = +>spreadUndefined : Symbol(spreadUndefined, Decl(objectSpread.ts, 39, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 39, 22)) + + { a: 7, ...undefined } +>a : Symbol(a, Decl(objectSpread.ts, 40, 5)) + +// methods are not enumerable +class C { p = 1; m() { } } +>C : Symbol(C, Decl(objectSpread.ts, 40, 26)) +>p : Symbol(C.p, Decl(objectSpread.ts, 43, 9)) +>m : Symbol(C.m, Decl(objectSpread.ts, 43, 16)) + +let c: C = new C() +>c : Symbol(c, Decl(objectSpread.ts, 44, 3)) +>C : Symbol(C, Decl(objectSpread.ts, 40, 26)) +>C : Symbol(C, Decl(objectSpread.ts, 40, 26)) + +let spreadC: { p: number } = { ...c } +>spreadC : Symbol(spreadC, Decl(objectSpread.ts, 45, 3)) +>p : Symbol(p, Decl(objectSpread.ts, 45, 14)) + +// own methods are enumerable +let cplus: { p: number, plus(): void } = { ...c, plus() { return this.p + 1; } }; +>cplus : Symbol(cplus, Decl(objectSpread.ts, 48, 3)) +>p : Symbol(p, Decl(objectSpread.ts, 48, 12)) +>plus : Symbol(plus, Decl(objectSpread.ts, 48, 23)) +>plus : Symbol(plus, Decl(objectSpread.ts, 48, 48)) + +cplus.plus(); +>cplus.plus : Symbol(plus, Decl(objectSpread.ts, 48, 23)) +>cplus : Symbol(cplus, Decl(objectSpread.ts, 48, 3)) +>plus : Symbol(plus, Decl(objectSpread.ts, 48, 23)) + +// new field's type conflicting with existing field is OK +let changeTypeAfter: { a: string, b: string } = +>changeTypeAfter : Symbol(changeTypeAfter, Decl(objectSpread.ts, 52, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 52, 22)) +>b : Symbol(b, Decl(objectSpread.ts, 52, 33)) + + { ...o, a: 'wrong type?' } +>a : Symbol(a, Decl(objectSpread.ts, 53, 11)) + +let changeTypeBefore: { a: number, b: string } = +>changeTypeBefore : Symbol(changeTypeBefore, Decl(objectSpread.ts, 54, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 54, 23)) +>b : Symbol(b, Decl(objectSpread.ts, 54, 34)) + + { a: 'wrong type?', ...o }; +>a : Symbol(a, Decl(objectSpread.ts, 55, 5)) + +let changeTypeBoth: { a: string, b: number } = +>changeTypeBoth : Symbol(changeTypeBoth, Decl(objectSpread.ts, 56, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 56, 21)) +>b : Symbol(b, Decl(objectSpread.ts, 56, 32)) + + { ...o, ...swap }; + +// optional +let definiteBoolean: { sn: boolean }; +>definiteBoolean : Symbol(definiteBoolean, Decl(objectSpread.ts, 60, 3)) +>sn : Symbol(sn, Decl(objectSpread.ts, 60, 22)) + +let definiteString: { sn: string }; +>definiteString : Symbol(definiteString, Decl(objectSpread.ts, 61, 3)) +>sn : Symbol(sn, Decl(objectSpread.ts, 61, 21)) + +let optionalString: { sn?: string }; +>optionalString : Symbol(optionalString, Decl(objectSpread.ts, 62, 3)) +>sn : Symbol(sn, Decl(objectSpread.ts, 62, 21)) + +let optionalNumber: { sn?: number }; +>optionalNumber : Symbol(optionalNumber, Decl(objectSpread.ts, 63, 3)) +>sn : Symbol(sn, Decl(objectSpread.ts, 63, 21)) + +let optionalUnionStops: { sn: string | number | boolean } = { ...definiteBoolean, ...definiteString, ...optionalNumber }; +>optionalUnionStops : Symbol(optionalUnionStops, Decl(objectSpread.ts, 64, 3)) +>sn : Symbol(sn, Decl(objectSpread.ts, 64, 25)) + +let optionalUnionDuplicates: { sn: string | number } = { ...definiteBoolean, ...definiteString, ...optionalString, ...optionalNumber }; +>optionalUnionDuplicates : Symbol(optionalUnionDuplicates, Decl(objectSpread.ts, 65, 3)) +>sn : Symbol(sn, Decl(objectSpread.ts, 65, 30)) + +let allOptional: { sn?: string | number } = { ...optionalString, ...optionalNumber }; +>allOptional : Symbol(allOptional, Decl(objectSpread.ts, 66, 3)) +>sn : Symbol(sn, Decl(objectSpread.ts, 66, 18)) + +// computed property +let computedFirst: { a: number, b: string, "before everything": number } = +>computedFirst : Symbol(computedFirst, Decl(objectSpread.ts, 69, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 69, 20)) +>b : Symbol(b, Decl(objectSpread.ts, 69, 31)) + + { ['before everything']: 12, ...o, b: 'yes' } +>'before everything' : Symbol(['before everything'], Decl(objectSpread.ts, 70, 5)) +>b : Symbol(b, Decl(objectSpread.ts, 70, 38)) + +let computedMiddle: { a: number, b: string, c: boolean, "in the middle": number } = +>computedMiddle : Symbol(computedMiddle, Decl(objectSpread.ts, 71, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 71, 21)) +>b : Symbol(b, Decl(objectSpread.ts, 71, 32)) +>c : Symbol(c, Decl(objectSpread.ts, 71, 43)) + + { ...o, ['in the middle']: 13, b: 'maybe?', ...o2 } +>'in the middle' : Symbol(['in the middle'], Decl(objectSpread.ts, 72, 11)) +>b : Symbol(b, Decl(objectSpread.ts, 72, 34)) + +let computedAfter: { a: number, b: string, "at the end": number } = +>computedAfter : Symbol(computedAfter, Decl(objectSpread.ts, 73, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 73, 20)) +>b : Symbol(b, Decl(objectSpread.ts, 73, 31)) + + { ...o, b: 'yeah', ['at the end']: 14 } +>b : Symbol(b, Decl(objectSpread.ts, 74, 11)) +>'at the end' : Symbol(['at the end'], Decl(objectSpread.ts, 74, 22)) + +// shortcut syntax +let a = 12; +>a : Symbol(a, Decl(objectSpread.ts, 76, 3)) + +let shortCutted: { a: number, b: string } = { ...o, a } +>shortCutted : Symbol(shortCutted, Decl(objectSpread.ts, 77, 3)) +>a : Symbol(a, Decl(objectSpread.ts, 77, 18)) +>b : Symbol(b, Decl(objectSpread.ts, 77, 29)) +>a : Symbol(a, Decl(objectSpread.ts, 77, 51)) + +// generics +function f(t: T, u: U): { id: string, ...T, ...U } { +>f : Symbol(f, Decl(objectSpread.ts, 77, 55)) +>T : Symbol(T, Decl(objectSpread.ts, 80, 11)) +>U : Symbol(U, Decl(objectSpread.ts, 80, 13)) +>t : Symbol(t, Decl(objectSpread.ts, 80, 17)) +>T : Symbol(T, Decl(objectSpread.ts, 80, 11)) +>u : Symbol(u, Decl(objectSpread.ts, 80, 22)) +>U : Symbol(U, Decl(objectSpread.ts, 80, 13)) +>id : Symbol(id, Decl(objectSpread.ts, 80, 31)) +>T : Symbol(T, Decl(objectSpread.ts, 80, 11)) +>U : Symbol(U, Decl(objectSpread.ts, 80, 13)) + + return { id: 'id', ...t, ...u }; +>id : Symbol(id, Decl(objectSpread.ts, 81, 12)) +} +let exclusive: { id: string, a: number, b: string, c: string, d: boolean } = +>exclusive : Symbol(exclusive, Decl(objectSpread.ts, 83, 3)) +>id : Symbol(id, Decl(objectSpread.ts, 83, 16)) +>a : Symbol(a, Decl(objectSpread.ts, 83, 28)) +>b : Symbol(b, Decl(objectSpread.ts, 83, 39)) +>c : Symbol(c, Decl(objectSpread.ts, 83, 50)) +>d : Symbol(d, Decl(objectSpread.ts, 83, 61)) + + f({ a: 1, b: 'yes' }, { c: 'no', d: false }) +>f : Symbol(f, Decl(objectSpread.ts, 77, 55)) +>a : Symbol(a, Decl(objectSpread.ts, 84, 7)) +>b : Symbol(b, Decl(objectSpread.ts, 84, 13)) +>c : Symbol(c, Decl(objectSpread.ts, 84, 27)) +>d : Symbol(d, Decl(objectSpread.ts, 84, 36)) + +let overlap: { id: string, a: number, b: string } = +>overlap : Symbol(overlap, Decl(objectSpread.ts, 85, 3)) +>id : Symbol(id, Decl(objectSpread.ts, 85, 14)) +>a : Symbol(a, Decl(objectSpread.ts, 85, 26)) +>b : Symbol(b, Decl(objectSpread.ts, 85, 37)) + + f({ a: 1 }, { a: 2, b: 'extra' }) +>f : Symbol(f, Decl(objectSpread.ts, 77, 55)) +>a : Symbol(a, Decl(objectSpread.ts, 86, 7)) +>a : Symbol(a, Decl(objectSpread.ts, 86, 17)) +>b : Symbol(b, Decl(objectSpread.ts, 86, 23)) + +let overlapConflict: { id:string, a: string } = +>overlapConflict : Symbol(overlapConflict, Decl(objectSpread.ts, 87, 3)) +>id : Symbol(id, Decl(objectSpread.ts, 87, 22)) +>a : Symbol(a, Decl(objectSpread.ts, 87, 33)) + + f({ a: 1 }, { a: 'mismatch' }) +>f : Symbol(f, Decl(objectSpread.ts, 77, 55)) +>a : Symbol(a, Decl(objectSpread.ts, 88, 7)) +>a : Symbol(a, Decl(objectSpread.ts, 88, 17)) + +let overwriteId: { id: boolean, a: number, c: number, d: string } = +>overwriteId : Symbol(overwriteId, Decl(objectSpread.ts, 89, 3)) +>id : Symbol(id, Decl(objectSpread.ts, 89, 18)) +>a : Symbol(a, Decl(objectSpread.ts, 89, 31)) +>c : Symbol(c, Decl(objectSpread.ts, 89, 42)) +>d : Symbol(d, Decl(objectSpread.ts, 89, 53)) + + f({ a: 1, id: true }, { c: 1, d: 'no' }) +>f : Symbol(f, Decl(objectSpread.ts, 77, 55)) +>a : Symbol(a, Decl(objectSpread.ts, 90, 7)) +>id : Symbol(id, Decl(objectSpread.ts, 90, 13)) +>c : Symbol(c, Decl(objectSpread.ts, 90, 27)) +>d : Symbol(d, Decl(objectSpread.ts, 90, 33)) + +class D { m() { }; q = 2; } +>D : Symbol(D, Decl(objectSpread.ts, 90, 44)) +>m : Symbol(D.m, Decl(objectSpread.ts, 92, 9)) +>q : Symbol(D.q, Decl(objectSpread.ts, 92, 18)) + +let classesAreWrong: { id: string, ...C, ...D } = +>classesAreWrong : Symbol(classesAreWrong, Decl(objectSpread.ts, 93, 3)) +>id : Symbol(id, Decl(objectSpread.ts, 93, 22)) +>C : Symbol(C, Decl(objectSpread.ts, 40, 26)) +>D : Symbol(D, Decl(objectSpread.ts, 90, 44)) + + f(new C(), new D()) +>f : Symbol(f, Decl(objectSpread.ts, 77, 55)) +>C : Symbol(C, Decl(objectSpread.ts, 40, 26)) +>D : Symbol(D, Decl(objectSpread.ts, 90, 44)) + diff --git a/tests/baselines/reference/objectSpread.types b/tests/baselines/reference/objectSpread.types new file mode 100644 index 0000000000000..1a053a22670c0 --- /dev/null +++ b/tests/baselines/reference/objectSpread.types @@ -0,0 +1,530 @@ +=== tests/cases/conformance/types/spread/objectSpread.ts === +let o = { a: 1, b: 'no' } +>o : { a: number; b: string; } +>{ a: 1, b: 'no' } : { a: number; b: string; } +>a : number +>1 : 1 +>b : string +>'no' : "no" + +let o2 = { b: 'yes', c: true } +>o2 : { b: string; c: boolean; } +>{ b: 'yes', c: true } : { b: string; c: boolean; } +>b : string +>'yes' : "yes" +>c : boolean +>true : true + +let swap = { a: 'yes', b: -1 }; +>swap : { a: string; b: number; } +>{ a: 'yes', b: -1 } : { a: string; b: number; } +>a : string +>'yes' : "yes" +>b : number +>-1 : -1 +>1 : 1 + +let addAfter: { a: number, b: string, c: boolean } = +>addAfter : { a: number; b: string; c: boolean; } +>a : number +>b : string +>c : boolean + + { ...o, c: false } +>{ ...o, c: false } : { ...{ a: number; b: string; }; c: false; } +>o : any +>c : boolean +>false : false + +let addBefore: { a: number, b: string, c: boolean } = +>addBefore : { a: number; b: string; c: boolean; } +>a : number +>b : string +>c : boolean + + { c: false, ...o } +>{ c: false, ...o } : { c: false; ...{ a: number; b: string; } } +>c : boolean +>false : false +>o : any + +// Note: ignore still changes the order that properties are printed +let ignore: { a: number, b: string } = +>ignore : { a: number; b: string; } +>a : number +>b : string + + { b: 'ignored', ...o } +>{ b: 'ignored', ...o } : { b: string; ...{ a: number; b: string; } } +>b : string +>'ignored' : "ignored" +>o : any + +let override: { a: number, b: string } = +>override : { a: number; b: string; } +>a : number +>b : string + + { ...o, b: 'override' } +>{ ...o, b: 'override' } : { ...{ a: number; b: string; }; b: string; } +>o : any +>b : string +>'override' : "override" + +let nested: { a: number, b: boolean, c: string } = +>nested : { a: number; b: boolean; c: string; } +>a : number +>b : boolean +>c : string + + { ...{ a: 3, ...{ b: false, c: 'overriden' } }, c: 'whatever' } +>{ ...{ a: 3, ...{ b: false, c: 'overriden' } }, c: 'whatever' } : { ...{ a: number; ...{ b: boolean; c: string; } }; c: string; } +>{ a: 3, ...{ b: false, c: 'overriden' } } : { a: number; ...{ b: boolean; c: string; } } +>a : number +>3 : 3 +>{ b: false, c: 'overriden' } : { b: boolean; c: string; } +>b : boolean +>false : false +>c : string +>'overriden' : "overriden" +>c : string +>'whatever' : "whatever" + +let combined: { a: number, b: string, c: boolean } = +>combined : { a: number; b: string; c: boolean; } +>a : number +>b : string +>c : boolean + + { ...o, ...o2 } +>{ ...o, ...o2 } : { ...{ a: number; b: string; }; ...{ b: string; c: boolean; } } +>o : any +>o2 : any + +let combinedBefore: { a: number, b: string, c: boolean } = +>combinedBefore : { a: number; b: string; c: boolean; } +>a : number +>b : string +>c : boolean + + { b: 'ok', ...o, ...o2 } +>{ b: 'ok', ...o, ...o2 } : { b: string; ...{ a: number; b: string; }; ...{ b: string; c: boolean; } } +>b : string +>'ok' : "ok" +>o : any +>o2 : any + +let combinedMid: { a: number, b: string, c: boolean } = +>combinedMid : { a: number; b: string; c: boolean; } +>a : number +>b : string +>c : boolean + + { ...o, b: 'ok', ...o2 } +>{ ...o, b: 'ok', ...o2 } : { ...{ a: number; b: string; }; b: string; ...{ b: string; c: boolean; } } +>o : any +>b : string +>'ok' : "ok" +>o2 : any + +let combinedAfter: { a: number, b: string, c: boolean } = +>combinedAfter : { a: number; b: string; c: boolean; } +>a : number +>b : string +>c : boolean + + { ...o, ...o2, b: 'ok' } +>{ ...o, ...o2, b: 'ok' } : { ...{ a: number; b: string; }; ...{ b: string; c: boolean; }; b: string; } +>o : any +>o2 : any +>b : string +>'ok' : "ok" + +let combinedNested: { a: number, b: boolean, c: string, d: string } = +>combinedNested : { a: number; b: boolean; c: string; d: string; } +>a : number +>b : boolean +>c : string +>d : string + + { ...{ a: 4, ...{ b: false, c: 'overriden' } }, d: 'actually new', ...{ a: 5, d: 'maybe new' } } +>{ ...{ a: 4, ...{ b: false, c: 'overriden' } }, d: 'actually new', ...{ a: 5, d: 'maybe new' } } : { ...{ a: number; ...{ b: boolean; c: string; } }; d: string; ...{ a: number; d: string; } } +>{ a: 4, ...{ b: false, c: 'overriden' } } : { a: number; ...{ b: boolean; c: string; } } +>a : number +>4 : 4 +>{ b: false, c: 'overriden' } : { b: boolean; c: string; } +>b : boolean +>false : false +>c : string +>'overriden' : "overriden" +>d : string +>'actually new' : "actually new" +>{ a: 5, d: 'maybe new' } : { a: number; d: string; } +>a : number +>5 : 5 +>d : string +>'maybe new' : "maybe new" + +let combinedNestedChangeType: { a: number, b: boolean, c: number } = +>combinedNestedChangeType : { a: number; b: boolean; c: number; } +>a : number +>b : boolean +>c : number + + { ...{ a: 1, ...{ b: false, c: 'overriden' } }, c: -1 } +>{ ...{ a: 1, ...{ b: false, c: 'overriden' } }, c: -1 } : { ...{ a: number; ...{ b: boolean; c: string; } }; c: number; } +>{ a: 1, ...{ b: false, c: 'overriden' } } : { a: number; ...{ b: boolean; c: string; } } +>a : number +>1 : 1 +>{ b: false, c: 'overriden' } : { b: boolean; c: string; } +>b : boolean +>false : false +>c : string +>'overriden' : "overriden" +>c : number +>-1 : -1 +>1 : 1 + +let propertyNested: { a: { a: number, b: string } } = +>propertyNested : { a: { a: number; b: string; }; } +>a : { a: number; b: string; } +>a : number +>b : string + + { a: { ... o } } +>{ a: { ... o } } : { a: { ...{ a: number; b: string; } }; } +>a : { ...{ a: number; b: string; } } +>{ ... o } : { ...{ a: number; b: string; } } +>o : any + +// accessors don't copy the descriptor +// (which means that readonly getters become read/write properties) +let op = { get a () { return 6 } }; +>op : { readonly a: number; } +>{ get a () { return 6 } } : { readonly a: number; } +>a : number +>6 : 6 + +let getter: { a: number, c: number } = +>getter : { a: number; c: number; } +>a : number +>c : number + + { ...op, c: 7 } +>{ ...op, c: 7 } : { ...{ readonly a: number; }; c: number; } +>op : any +>c : number +>7 : 7 + +getter.a = 12; +>getter.a = 12 : 12 +>getter.a : number +>getter : { a: number; c: number; } +>a : number +>12 : 12 + +// null and undefined are just skipped +let spreadNull: { a: number } = +>spreadNull : { a: number; } +>a : number + + { a: 7, ...null } +>{ a: 7, ...null } : { a: number; } +>a : number +>7 : 7 +>null : null + +let spreadUndefined: { a: number } = +>spreadUndefined : { a: number; } +>a : number + + { a: 7, ...undefined } +>{ a: 7, ...undefined } : { a: number; } +>a : number +>7 : 7 +>undefined : any + +// methods are not enumerable +class C { p = 1; m() { } } +>C : C +>p : number +>1 : 1 +>m : () => void + +let c: C = new C() +>c : C +>C : C +>new C() : C +>C : typeof C + +let spreadC: { p: number } = { ...c } +>spreadC : { p: number; } +>p : number +>{ ...c } : { ...C } +>c : any + +// own methods are enumerable +let cplus: { p: number, plus(): void } = { ...c, plus() { return this.p + 1; } }; +>cplus : { p: number; plus(): void; } +>p : number +>plus : () => void +>{ ...c, plus() { return this.p + 1; } } : { ...C; plus(): any; } +>c : any +>plus : () => any +>this.p + 1 : any +>this.p : any +>this : any +>p : any +>1 : 1 + +cplus.plus(); +>cplus.plus() : void +>cplus.plus : () => void +>cplus : { p: number; plus(): void; } +>plus : () => void + +// new field's type conflicting with existing field is OK +let changeTypeAfter: { a: string, b: string } = +>changeTypeAfter : { a: string; b: string; } +>a : string +>b : string + + { ...o, a: 'wrong type?' } +>{ ...o, a: 'wrong type?' } : { ...{ a: number; b: string; }; a: string; } +>o : any +>a : string +>'wrong type?' : "wrong type?" + +let changeTypeBefore: { a: number, b: string } = +>changeTypeBefore : { a: number; b: string; } +>a : number +>b : string + + { a: 'wrong type?', ...o }; +>{ a: 'wrong type?', ...o } : { a: string; ...{ a: number; b: string; } } +>a : string +>'wrong type?' : "wrong type?" +>o : any + +let changeTypeBoth: { a: string, b: number } = +>changeTypeBoth : { a: string; b: number; } +>a : string +>b : number + + { ...o, ...swap }; +>{ ...o, ...swap } : { ...{ a: number; b: string; }; ...{ a: string; b: number; } } +>o : any +>swap : any + +// optional +let definiteBoolean: { sn: boolean }; +>definiteBoolean : { sn: boolean; } +>sn : boolean + +let definiteString: { sn: string }; +>definiteString : { sn: string; } +>sn : string + +let optionalString: { sn?: string }; +>optionalString : { sn?: string; } +>sn : string + +let optionalNumber: { sn?: number }; +>optionalNumber : { sn?: number; } +>sn : number + +let optionalUnionStops: { sn: string | number | boolean } = { ...definiteBoolean, ...definiteString, ...optionalNumber }; +>optionalUnionStops : { sn: string | number | boolean; } +>sn : string | number | boolean +>{ ...definiteBoolean, ...definiteString, ...optionalNumber } : { ...{ sn: boolean; }; ...{ sn: string; }; ...{ sn?: number; } } +>definiteBoolean : any +>definiteString : any +>optionalNumber : any + +let optionalUnionDuplicates: { sn: string | number } = { ...definiteBoolean, ...definiteString, ...optionalString, ...optionalNumber }; +>optionalUnionDuplicates : { sn: string | number; } +>sn : string | number +>{ ...definiteBoolean, ...definiteString, ...optionalString, ...optionalNumber } : { ...{ sn: boolean; }; ...{ sn: string; }; ...{ sn?: string; }; ...{ sn?: number; } } +>definiteBoolean : any +>definiteString : any +>optionalString : any +>optionalNumber : any + +let allOptional: { sn?: string | number } = { ...optionalString, ...optionalNumber }; +>allOptional : { sn?: string | number; } +>sn : string | number +>{ ...optionalString, ...optionalNumber } : { ...{ sn?: string; }; ...{ sn?: number; } } +>optionalString : any +>optionalNumber : any + +// computed property +let computedFirst: { a: number, b: string, "before everything": number } = +>computedFirst : { a: number; b: string; "before everything": number; } +>a : number +>b : string + + { ['before everything']: 12, ...o, b: 'yes' } +>{ ['before everything']: 12, ...o, b: 'yes' } : { ['before everything']: number; ...{ a: number; b: string; }; b: string; } +>'before everything' : "before everything" +>12 : 12 +>o : any +>b : string +>'yes' : "yes" + +let computedMiddle: { a: number, b: string, c: boolean, "in the middle": number } = +>computedMiddle : { a: number; b: string; c: boolean; "in the middle": number; } +>a : number +>b : string +>c : boolean + + { ...o, ['in the middle']: 13, b: 'maybe?', ...o2 } +>{ ...o, ['in the middle']: 13, b: 'maybe?', ...o2 } : { ...{ a: number; b: string; }; ['in the middle']: number; b: string; ...{ b: string; c: boolean; } } +>o : any +>'in the middle' : "in the middle" +>13 : 13 +>b : string +>'maybe?' : "maybe?" +>o2 : any + +let computedAfter: { a: number, b: string, "at the end": number } = +>computedAfter : { a: number; b: string; "at the end": number; } +>a : number +>b : string + + { ...o, b: 'yeah', ['at the end']: 14 } +>{ ...o, b: 'yeah', ['at the end']: 14 } : { ...{ a: number; b: string; }; b: string; ['at the end']: number; } +>o : any +>b : string +>'yeah' : "yeah" +>'at the end' : "at the end" +>14 : 14 + +// shortcut syntax +let a = 12; +>a : number +>12 : 12 + +let shortCutted: { a: number, b: string } = { ...o, a } +>shortCutted : { a: number; b: string; } +>a : number +>b : string +>{ ...o, a } : { ...{ a: number; b: string; }; a: number; } +>o : any +>a : number + +// generics +function f(t: T, u: U): { id: string, ...T, ...U } { +>f : (t: T, u: U) => { id: string; ...T; ...U } +>T : T +>U : U +>t : T +>T : T +>u : U +>U : U +>id : string +>T : T +>U : U + + return { id: 'id', ...t, ...u }; +>{ id: 'id', ...t, ...u } : { id: string; ...T; ...U } +>id : string +>'id' : "id" +>t : any +>u : any +} +let exclusive: { id: string, a: number, b: string, c: string, d: boolean } = +>exclusive : { id: string; a: number; b: string; c: string; d: boolean; } +>id : string +>a : number +>b : string +>c : string +>d : boolean + + f({ a: 1, b: 'yes' }, { c: 'no', d: false }) +>f({ a: 1, b: 'yes' }, { c: 'no', d: false }) : { id: string; ...{ a: number; b: string; }; ...{ c: string; d: boolean; } } +>f : (t: T, u: U) => { id: string; ...T; ...U } +>{ a: 1, b: 'yes' } : { a: number; b: string; } +>a : number +>1 : 1 +>b : string +>'yes' : "yes" +>{ c: 'no', d: false } : { c: string; d: false; } +>c : string +>'no' : "no" +>d : boolean +>false : false + +let overlap: { id: string, a: number, b: string } = +>overlap : { id: string; a: number; b: string; } +>id : string +>a : number +>b : string + + f({ a: 1 }, { a: 2, b: 'extra' }) +>f({ a: 1 }, { a: 2, b: 'extra' }) : { id: string; ...{ a: number; }; ...{ a: number; b: string; } } +>f : (t: T, u: U) => { id: string; ...T; ...U } +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 +>{ a: 2, b: 'extra' } : { a: number; b: string; } +>a : number +>2 : 2 +>b : string +>'extra' : "extra" + +let overlapConflict: { id:string, a: string } = +>overlapConflict : { id: string; a: string; } +>id : string +>a : string + + f({ a: 1 }, { a: 'mismatch' }) +>f({ a: 1 }, { a: 'mismatch' }) : { id: string; ...{ a: number; }; ...{ a: string; } } +>f : (t: T, u: U) => { id: string; ...T; ...U } +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 +>{ a: 'mismatch' } : { a: string; } +>a : string +>'mismatch' : "mismatch" + +let overwriteId: { id: boolean, a: number, c: number, d: string } = +>overwriteId : { id: boolean; a: number; c: number; d: string; } +>id : boolean +>a : number +>c : number +>d : string + + f({ a: 1, id: true }, { c: 1, d: 'no' }) +>f({ a: 1, id: true }, { c: 1, d: 'no' }) : { id: string; ...{ a: number; id: boolean; }; ...{ c: number; d: string; } } +>f : (t: T, u: U) => { id: string; ...T; ...U } +>{ a: 1, id: true } : { a: number; id: true; } +>a : number +>1 : 1 +>id : boolean +>true : true +>{ c: 1, d: 'no' } : { c: number; d: string; } +>c : number +>1 : 1 +>d : string +>'no' : "no" + +class D { m() { }; q = 2; } +>D : D +>m : () => void +>q : number +>2 : 2 + +let classesAreWrong: { id: string, ...C, ...D } = +>classesAreWrong : { id: string; ...C; ...D } +>id : string +>C : C +>D : D + + f(new C(), new D()) +>f(new C(), new D()) : { id: string; ...C; ...D } +>f : (t: T, u: U) => { id: string; ...T; ...U } +>new C() : C +>C : typeof C +>new D() : D +>D : typeof D + diff --git a/tests/baselines/reference/objectSpreadGeneric.errors.txt b/tests/baselines/reference/objectSpreadGeneric.errors.txt new file mode 100644 index 0000000000000..16dfc835d2607 --- /dev/null +++ b/tests/baselines/reference/objectSpreadGeneric.errors.txt @@ -0,0 +1,80 @@ +tests/cases/conformance/types/spread/objectSpreadGeneric.ts(4,11): error TS2322: Type '{ ...T; ...U; ...V }' is not assignable to type '{ ...V; ...U; ...T }'. +tests/cases/conformance/types/spread/objectSpreadGeneric.ts(5,11): error TS2322: Type '{ ...T; ...U; ...V }' is not assignable to type '{ ...U; ...T; ...V }'. +tests/cases/conformance/types/spread/objectSpreadGeneric.ts(6,11): error TS2322: Type '{ ...T; ...U; ...V }' is not assignable to type '{ ...U; ...V }'. +tests/cases/conformance/types/spread/objectSpreadGeneric.ts(7,11): error TS2322: Type '{ ...T; ...U; ...V }' is not assignable to type '{ ...T; ...V }'. +tests/cases/conformance/types/spread/objectSpreadGeneric.ts(8,11): error TS2322: Type '{ ...T; ...U; ...V }' is not assignable to type '{ ...T; ...U }'. +tests/cases/conformance/types/spread/objectSpreadGeneric.ts(13,11): error TS2322: Type '{}' is not assignable to type '{ ...T; ...U }'. +tests/cases/conformance/types/spread/objectSpreadGeneric.ts(26,11): error TS2322: Type '{ first: string; ...T; second: string; ...U; third: string; }' is not assignable to type '{ first: string; second: string; ...T; third: string; }'. +tests/cases/conformance/types/spread/objectSpreadGeneric.ts(34,11): error TS2322: Type '{ firrrrrrst: string; ...T; second: string; ...U; third: string; }' is not assignable to type '{ first: string; ...T; second: string; ...U; third: string; }'. + Property 'first' is missing in type '{ firrrrrrst: string; ...T; second: string; ...U; third: string; }'. +tests/cases/conformance/types/spread/objectSpreadGeneric.ts(36,11): error TS2322: Type '{ first: string; ...T; ssssssssecond: string; ...U; third: string; }' is not assignable to type '{ first: string; ...T; second: string; ...U; third: string; }'. + Property 'second' is missing in type '{ first: string; ...T; ssssssssecond: string; ...U; third: string; }'. +tests/cases/conformance/types/spread/objectSpreadGeneric.ts(38,11): error TS2322: Type '{ first: string; ...T; second: string; ...U; thirrrrrrrd: string; }' is not assignable to type '{ first: string; ...T; second: string; ...U; third: string; }'. + Property 'third' is missing in type '{ first: string; ...T; second: string; ...U; thirrrrrrrd: string; }'. + + +==== tests/cases/conformance/types/spread/objectSpreadGeneric.ts (10 errors) ==== + function f(t: T, u: U, v: V): void { + let o: { ...T, ...U, ...V }; + const same: { ...T, ...U, ...V } = o; // ok + const reversed: { ...V, ...U, ...T } = o; // error, reversed + ~~~~~~~~ +!!! error TS2322: Type '{ ...T; ...U; ...V }' is not assignable to type '{ ...V; ...U; ...T }'. + const reversed2: { ...U, ...T, ...V } = o; // error, U and T are still reversed + ~~~~~~~~~ +!!! error TS2322: Type '{ ...T; ...U; ...V }' is not assignable to type '{ ...U; ...T; ...V }'. + const missingT: { ...U, ...V } = o; // error, missing T + ~~~~~~~~ +!!! error TS2322: Type '{ ...T; ...U; ...V }' is not assignable to type '{ ...U; ...V }'. + const missingU: { ...T, ...V } = o; // error, missing U + ~~~~~~~~ +!!! error TS2322: Type '{ ...T; ...U; ...V }' is not assignable to type '{ ...T; ...V }'. + const missingV: { ...T, ...U } = o; // error, missing V + ~~~~~~~~ +!!! error TS2322: Type '{ ...T; ...U; ...V }' is not assignable to type '{ ...T; ...U }'. + const atEnd: { ...T, ...U, second: string } = { ...t, ...u, second: 'foo' }; // ok + const atBeginning: { first: string, ...T, ...U, } = { first: 'foo', ...t, ...u }; // ok + + const emptyTarget: { } = { ...t, ...u } // ok + const emptySource: { ...T, ...U } = { }; // error, {} is not assignable to U (or T) + ~~~~~~~~~~~ +!!! error TS2322: Type '{}' is not assignable to type '{ ...T; ...U }'. + + let optionalNumber: { sn?: number }; + let optionalString: { sn?: string }; + let optionalBoolean: { sn?: boolean }; + const unionCutoff: { ...T, sn?: number | string | boolean } = + { ...optionalBoolean, ...t, ...optionalString, ...optionalNumber } // ok + unionCutoff.sn; // ok + const optionalCutoff = { ...t, ...optionalNumber }; // ok + optionalCutoff.sn; // ok + + const interspersed: { first: string, ...T, second: string, ...U, third: string } = + { first: '1', ...t, second: '2', ...u, third: '3' }; // ok + const interspersedMissingU: { first: string, second: string, ...T, third: string } = + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ first: string; ...T; second: string; ...U; third: string; }' is not assignable to type '{ first: string; second: string; ...T; third: string; }'. + { first: '1', ...t, second: '2', ...u, third: '3' }; // error, 'U' is missing + const interspersedOrder1: { first: string, ...T, second: string, ...U, third: string, secondsecond: string } = + { first: '1', ...t, second: '2', ...u, third: '3', secondsecond: 'false' }; // ok + const interspersedOrder2: { first: string, second: string, secondsecond: string, third: string, ...T, ...U } = + { first: '1', ...t, second: '2', ...u, third: '3', secondsecond: 'false' }; // ok + + + const mismatchFirst: { first: string, ...T, second: string, ...U, third: string } = + ~~~~~~~~~~~~~ +!!! error TS2322: Type '{ firrrrrrst: string; ...T; second: string; ...U; third: string; }' is not assignable to type '{ first: string; ...T; second: string; ...U; third: string; }'. +!!! error TS2322: Property 'first' is missing in type '{ firrrrrrst: string; ...T; second: string; ...U; third: string; }'. + { firrrrrrst: '1', ...t, second: '2', ...u, third: '3' }; // error, 'first' not found + const mismatchSecond: { first: string, ...T, second: string, ...U, third: string } = + ~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ first: string; ...T; ssssssssecond: string; ...U; third: string; }' is not assignable to type '{ first: string; ...T; second: string; ...U; third: string; }'. +!!! error TS2322: Property 'second' is missing in type '{ first: string; ...T; ssssssssecond: string; ...U; third: string; }'. + { first: '1', ...t, ssssssssecond: '2', ...u, third: '3' }; // error, 'second' not found + const mismatchLast: { first: string, ...T, second: string, ...U, third: string } = + ~~~~~~~~~~~~ +!!! error TS2322: Type '{ first: string; ...T; second: string; ...U; thirrrrrrrd: string; }' is not assignable to type '{ first: string; ...T; second: string; ...U; third: string; }'. +!!! error TS2322: Property 'third' is missing in type '{ first: string; ...T; second: string; ...U; thirrrrrrrd: string; }'. + { first: '1', ...t, second: '2', ...u, thirrrrrrrd: '3' }; // error, 'third' not found + } + \ No newline at end of file diff --git a/tests/baselines/reference/objectSpreadGeneric.js b/tests/baselines/reference/objectSpreadGeneric.js new file mode 100644 index 0000000000000..593ab73f98fac --- /dev/null +++ b/tests/baselines/reference/objectSpreadGeneric.js @@ -0,0 +1,79 @@ +//// [objectSpreadGeneric.ts] +function f(t: T, u: U, v: V): void { + let o: { ...T, ...U, ...V }; + const same: { ...T, ...U, ...V } = o; // ok + const reversed: { ...V, ...U, ...T } = o; // error, reversed + const reversed2: { ...U, ...T, ...V } = o; // error, U and T are still reversed + const missingT: { ...U, ...V } = o; // error, missing T + const missingU: { ...T, ...V } = o; // error, missing U + const missingV: { ...T, ...U } = o; // error, missing V + const atEnd: { ...T, ...U, second: string } = { ...t, ...u, second: 'foo' }; // ok + const atBeginning: { first: string, ...T, ...U, } = { first: 'foo', ...t, ...u }; // ok + + const emptyTarget: { } = { ...t, ...u } // ok + const emptySource: { ...T, ...U } = { }; // error, {} is not assignable to U (or T) + + let optionalNumber: { sn?: number }; + let optionalString: { sn?: string }; + let optionalBoolean: { sn?: boolean }; + const unionCutoff: { ...T, sn?: number | string | boolean } = + { ...optionalBoolean, ...t, ...optionalString, ...optionalNumber } // ok + unionCutoff.sn; // ok + const optionalCutoff = { ...t, ...optionalNumber }; // ok + optionalCutoff.sn; // ok + + const interspersed: { first: string, ...T, second: string, ...U, third: string } = + { first: '1', ...t, second: '2', ...u, third: '3' }; // ok + const interspersedMissingU: { first: string, second: string, ...T, third: string } = + { first: '1', ...t, second: '2', ...u, third: '3' }; // error, 'U' is missing + const interspersedOrder1: { first: string, ...T, second: string, ...U, third: string, secondsecond: string } = + { first: '1', ...t, second: '2', ...u, third: '3', secondsecond: 'false' }; // ok + const interspersedOrder2: { first: string, second: string, secondsecond: string, third: string, ...T, ...U } = + { first: '1', ...t, second: '2', ...u, third: '3', secondsecond: 'false' }; // ok + + + const mismatchFirst: { first: string, ...T, second: string, ...U, third: string } = + { firrrrrrst: '1', ...t, second: '2', ...u, third: '3' }; // error, 'first' not found + const mismatchSecond: { first: string, ...T, second: string, ...U, third: string } = + { first: '1', ...t, ssssssssecond: '2', ...u, third: '3' }; // error, 'second' not found + const mismatchLast: { first: string, ...T, second: string, ...U, third: string } = + { first: '1', ...t, second: '2', ...u, thirrrrrrrd: '3' }; // error, 'third' not found +} + + +//// [objectSpreadGeneric.js] +var __assign = (this && this.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; +}; +function f(t, u, v) { + var o; + var same = o; // ok + var reversed = o; // error, reversed + var reversed2 = o; // error, U and T are still reversed + var missingT = o; // error, missing T + var missingU = o; // error, missing U + var missingV = o; // error, missing V + var atEnd = __assign({}, t, u, { second: 'foo' }); // ok + var atBeginning = __assign({ first: 'foo' }, t, u); // ok + var emptyTarget = __assign({}, t, u); // ok + var emptySource = {}; // error, {} is not assignable to U (or T) + var optionalNumber; + var optionalString; + var optionalBoolean; + var unionCutoff = __assign({}, optionalBoolean, t, optionalString, optionalNumber); // ok + unionCutoff.sn; // ok + var optionalCutoff = __assign({}, t, optionalNumber); // ok + optionalCutoff.sn; // ok + var interspersed = __assign({ first: '1' }, t, { second: '2' }, u, { third: '3' }); // ok + var interspersedMissingU = __assign({ first: '1' }, t, { second: '2' }, u, { third: '3' }); // error, 'U' is missing + var interspersedOrder1 = __assign({ first: '1' }, t, { second: '2' }, u, { third: '3', secondsecond: 'false' }); // ok + var interspersedOrder2 = __assign({ first: '1' }, t, { second: '2' }, u, { third: '3', secondsecond: 'false' }); // ok + var mismatchFirst = __assign({ firrrrrrst: '1' }, t, { second: '2' }, u, { third: '3' }); // error, 'first' not found + var mismatchSecond = __assign({ first: '1' }, t, { ssssssssecond: '2' }, u, { third: '3' }); // error, 'second' not found + var mismatchLast = __assign({ first: '1' }, t, { second: '2' }, u, { thirrrrrrrd: '3' }); // error, 'third' not found +} diff --git a/tests/baselines/reference/objectSpreadNegative.errors.txt b/tests/baselines/reference/objectSpreadNegative.errors.txt new file mode 100644 index 0000000000000..1ee3acb823c6a --- /dev/null +++ b/tests/baselines/reference/objectSpreadNegative.errors.txt @@ -0,0 +1,93 @@ +tests/cases/conformance/types/spread/objectSpreadNegative.ts(13,30): error TS2339: Property 'x' does not exist on type '{ ...PublicX; ...PrivateOptionalX }'. +tests/cases/conformance/types/spread/objectSpreadNegative.ts(16,5): error TS2322: Type '{ ...{ sn?: string; }; ...{ sn?: number; } }' is not assignable to type '{ sn: string | number; }'. + Property 'sn' is optional in type '{ ...{ sn?: string; }; ...{ sn?: number; } }' but required in type '{ sn: string | number; }'. +tests/cases/conformance/types/spread/objectSpreadNegative.ts(22,5): error TS2322: Type '{ s: string; }' is not assignable to type '{ ...Bool; ...Str }'. +tests/cases/conformance/types/spread/objectSpreadNegative.ts(24,1): error TS2322: Type 'Bool' is not assignable to type '{ ...Bool; ...Str }'. +tests/cases/conformance/types/spread/objectSpreadNegative.ts(27,15): error TS2697: Spread properties must be identifiers, property accesses, or object literals. +tests/cases/conformance/types/spread/objectSpreadNegative.ts(28,15): error TS2697: Spread properties must be identifiers, property accesses, or object literals. +tests/cases/conformance/types/spread/objectSpreadNegative.ts(31,36): error TS2300: Duplicate identifier 'b'. +tests/cases/conformance/types/spread/objectSpreadNegative.ts(31,53): error TS2300: Duplicate identifier 'b'. +tests/cases/conformance/types/spread/objectSpreadNegative.ts(36,12): error TS2339: Property 'b' does not exist on type '{ ...{ b: number; } }'. +tests/cases/conformance/types/spread/objectSpreadNegative.ts(42,9): error TS2339: Property 'm' does not exist on type '{ ...C }'. +tests/cases/conformance/types/spread/objectSpreadNegative.ts(45,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '{ ...PublicX }' has no compatible call signatures. +tests/cases/conformance/types/spread/objectSpreadNegative.ts(46,1): error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature. +tests/cases/conformance/types/spread/objectSpreadNegative.ts(48,39): error TS2697: Spread properties must be identifiers, property accesses, or object literals. + + +==== tests/cases/conformance/types/spread/objectSpreadNegative.ts (13 errors) ==== + let o = { a: 1, b: 'no' } + + /// private propagates + class PrivateOptionalX { + private x?: number; + } + class PublicX { + public x: number; + } + let privateOptionalx: PrivateOptionalX; + let publicx: PublicX; + let o3 = { ...publicx, ...privateOptionalx }; + let sn: string | number = o3.x; // error, x is private + ~ +!!! error TS2339: Property 'x' does not exist on type '{ ...PublicX; ...PrivateOptionalX }'. + let optionalString: { sn?: string }; + let optionalNumber: { sn?: number }; + let allOptional: { sn: string | number } = { ...optionalString, ...optionalNumber }; + ~~~~~~~~~~~ +!!! error TS2322: Type '{ ...{ sn?: string; }; ...{ sn?: number; } }' is not assignable to type '{ sn: string | number; }'. +!!! error TS2322: Property 'sn' is optional in type '{ ...{ sn?: string; }; ...{ sn?: number; } }' but required in type '{ sn: string | number; }'. + // error, 'sn' is optional in source, required in target + + // assignability as target + interface Bool { b: boolean }; + interface Str { s: string }; + let spread: { ...Bool, ...Str } = { s: 'foo' }; // error, missing 'b' + ~~~~~~ +!!! error TS2322: Type '{ s: string; }' is not assignable to type '{ ...Bool; ...Str }'. + let b: Bool; + spread = b; // error, missing 's' + ~~~~~~ +!!! error TS2322: Type 'Bool' is not assignable to type '{ ...Bool; ...Str }'. + + // expressions are not allowed + let o1 = { ...1 + 1 }; + ~~~~~ +!!! error TS2697: Spread properties must be identifiers, property accesses, or object literals. + let o2 = { ...(1 + 1) }; + ~~~~~~~ +!!! error TS2697: Spread properties must be identifiers, property accesses, or object literals. + + // literal repeats are not allowed, but spread repeats are fine + let duplicated = { b: 'bad', ...o, b: 'bad', ...o2, b: 'bad' } + ~ +!!! error TS2300: Duplicate identifier 'b'. + ~ +!!! error TS2300: Duplicate identifier 'b'. + let duplicatedSpread = { ...o, ...o } + + // write-only properties get skipped + let setterOnly = { ...{ set b (bad: number) { } } }; + setterOnly.b = 12; // error, 'b' does not exist + ~ +!!! error TS2339: Property 'b' does not exist on type '{ ...{ b: number; } }'. + + // methods are skipped because they aren't enumerable + class C { p = 1; m() { } } + let c: C = new C() + let spreadC = { ...c } + spreadC.m(); // error 'm' is not in '{ ... c }' + ~ +!!! error TS2339: Property 'm' does not exist on type '{ ...C }'. + + let callableConstructableSpread: { ...PublicX, (n: number): number, new (p: number) }; + callableConstructableSpread(12); // error, no call signature + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '{ ...PublicX }' has no compatible call signatures. + new callableConstructableSpread(12); // error, no construct signature + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature. + + let callableSpread = { ...publicx, ...(n => n + 1) }; // error, can't spread functions + ~~~~~~~~~~~~ +!!! error TS2697: Spread properties must be identifiers, property accesses, or object literals. + \ No newline at end of file diff --git a/tests/baselines/reference/objectSpreadNegative.js b/tests/baselines/reference/objectSpreadNegative.js new file mode 100644 index 0000000000000..685760f944467 --- /dev/null +++ b/tests/baselines/reference/objectSpreadNegative.js @@ -0,0 +1,108 @@ +//// [objectSpreadNegative.ts] +let o = { a: 1, b: 'no' } + +/// private propagates +class PrivateOptionalX { + private x?: number; +} +class PublicX { + public x: number; +} +let privateOptionalx: PrivateOptionalX; +let publicx: PublicX; +let o3 = { ...publicx, ...privateOptionalx }; +let sn: string | number = o3.x; // error, x is private +let optionalString: { sn?: string }; +let optionalNumber: { sn?: number }; +let allOptional: { sn: string | number } = { ...optionalString, ...optionalNumber }; +// error, 'sn' is optional in source, required in target + +// assignability as target +interface Bool { b: boolean }; +interface Str { s: string }; +let spread: { ...Bool, ...Str } = { s: 'foo' }; // error, missing 'b' +let b: Bool; +spread = b; // error, missing 's' + +// expressions are not allowed +let o1 = { ...1 + 1 }; +let o2 = { ...(1 + 1) }; + +// literal repeats are not allowed, but spread repeats are fine +let duplicated = { b: 'bad', ...o, b: 'bad', ...o2, b: 'bad' } +let duplicatedSpread = { ...o, ...o } + +// write-only properties get skipped +let setterOnly = { ...{ set b (bad: number) { } } }; +setterOnly.b = 12; // error, 'b' does not exist + +// methods are skipped because they aren't enumerable +class C { p = 1; m() { } } +let c: C = new C() +let spreadC = { ...c } +spreadC.m(); // error 'm' is not in '{ ... c }' + +let callableConstructableSpread: { ...PublicX, (n: number): number, new (p: number) }; +callableConstructableSpread(12); // error, no call signature +new callableConstructableSpread(12); // error, no construct signature + +let callableSpread = { ...publicx, ...(n => n + 1) }; // error, can't spread functions + + +//// [objectSpreadNegative.js] +var __assign = (this && this.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; +}; +var o = { a: 1, b: 'no' }; +/// private propagates +var PrivateOptionalX = (function () { + function PrivateOptionalX() { + } + return PrivateOptionalX; +}()); +var PublicX = (function () { + function PublicX() { + } + return PublicX; +}()); +var privateOptionalx; +var publicx; +var o3 = __assign({}, publicx, privateOptionalx); +var sn = o3.x; // error, x is private +var optionalString; +var optionalNumber; +var allOptional = __assign({}, optionalString, optionalNumber); +; +; +var spread = { s: 'foo' }; // error, missing 'b' +var b; +spread = b; // error, missing 's' +// expressions are not allowed +var o1 = __assign({}, 1 + 1); +var o2 = __assign({}, (1 + 1)); +// literal repeats are not allowed, but spread repeats are fine +var duplicated = __assign({ b: 'bad' }, o, { b: 'bad' }, o2, { b: 'bad' }); +var duplicatedSpread = __assign({}, o, o); +// write-only properties get skipped +var setterOnly = __assign({ set b(bad: number) { } }); +setterOnly.b = 12; // error, 'b' does not exist +// methods are skipped because they aren't enumerable +var C = (function () { + function C() { + this.p = 1; + } + C.prototype.m = function () { }; + return C; +}()); +var c = new C(); +var spreadC = __assign({}, c); +spreadC.m(); // error 'm' is not in '{ ... c }' +var callableConstructableSpread; +callableConstructableSpread(12); // error, no call signature +new callableConstructableSpread(12); // error, no construct signature +var callableSpread = __assign({}, publicx, (function (n) { return n + 1; })); // error, can't spread functions diff --git a/tests/baselines/reference/objectSpreadNegativeParse.errors.txt b/tests/baselines/reference/objectSpreadNegativeParse.errors.txt new file mode 100644 index 0000000000000..41651fb1d1c8e --- /dev/null +++ b/tests/baselines/reference/objectSpreadNegativeParse.errors.txt @@ -0,0 +1,35 @@ +tests/cases/conformance/types/spread/objectSpreadNegativeParse.ts(1,15): error TS2304: Cannot find name 'o'. +tests/cases/conformance/types/spread/objectSpreadNegativeParse.ts(1,18): error TS1109: Expression expected. +tests/cases/conformance/types/spread/objectSpreadNegativeParse.ts(2,15): error TS1109: Expression expected. +tests/cases/conformance/types/spread/objectSpreadNegativeParse.ts(2,16): error TS2304: Cannot find name 'o'. +tests/cases/conformance/types/spread/objectSpreadNegativeParse.ts(3,15): error TS2304: Cannot find name 'matchMedia'. +tests/cases/conformance/types/spread/objectSpreadNegativeParse.ts(3,28): error TS1005: ',' expected. +tests/cases/conformance/types/spread/objectSpreadNegativeParse.ts(3,31): error TS1128: Declaration or statement expected. +tests/cases/conformance/types/spread/objectSpreadNegativeParse.ts(4,16): error TS2304: Cannot find name 'get'. +tests/cases/conformance/types/spread/objectSpreadNegativeParse.ts(4,20): error TS1005: ',' expected. + + +==== tests/cases/conformance/types/spread/objectSpreadNegativeParse.ts (9 errors) ==== + let o7 = { ...o? }; + ~ +!!! error TS2304: Cannot find name 'o'. + ~ +!!! error TS1109: Expression expected. + let o8 = { ...*o }; + ~ +!!! error TS1109: Expression expected. + ~ +!!! error TS2304: Cannot find name 'o'. + let o9 = { ...matchMedia() { }}; + ~~~~~~~~~~ +!!! error TS2304: Cannot find name 'matchMedia'. + ~ +!!! error TS1005: ',' expected. + ~ +!!! error TS1128: Declaration or statement expected. + let o10 = { ...get x() { return 12; }}; + ~~~ +!!! error TS2304: Cannot find name 'get'. + ~ +!!! error TS1005: ',' expected. + \ No newline at end of file diff --git a/tests/baselines/reference/objectSpreadNegativeParse.js b/tests/baselines/reference/objectSpreadNegativeParse.js new file mode 100644 index 0000000000000..297c56c3e62ae --- /dev/null +++ b/tests/baselines/reference/objectSpreadNegativeParse.js @@ -0,0 +1,21 @@ +//// [objectSpreadNegativeParse.ts] +let o7 = { ...o? }; +let o8 = { ...*o }; +let o9 = { ...matchMedia() { }}; +let o10 = { ...get x() { return 12; }}; + + +//// [objectSpreadNegativeParse.js] +var __assign = (this && this.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; +}; +var o7 = __assign({}, o ? : ); +var o8 = __assign({}, * o); +var o9 = __assign({}, matchMedia()), _a = void 0; +; +var o10 = __assign({}, get, { x: function () { return 12; } }); From 39385590b6b9571d85ab74b43faaf7caddd9c388 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 26 Sep 2016 09:43:59 -0700 Subject: [PATCH 006/224] Move interfaceSpread to correct location --- tests/baselines/reference/interfaceSpread.errors.txt | 8 ++++---- .../destructuring => types/spread}/interfaceSpread.ts | 0 2 files changed, 4 insertions(+), 4 deletions(-) rename tests/cases/conformance/{es6/destructuring => types/spread}/interfaceSpread.ts (100%) diff --git a/tests/baselines/reference/interfaceSpread.errors.txt b/tests/baselines/reference/interfaceSpread.errors.txt index 27aab11491c5a..2b27c972cc895 100644 --- a/tests/baselines/reference/interfaceSpread.errors.txt +++ b/tests/baselines/reference/interfaceSpread.errors.txt @@ -1,9 +1,9 @@ -tests/cases/conformance/es6/destructuring/interfaceSpread.ts(2,5): error TS2699: Interface declaration cannot contain a spread property. -tests/cases/conformance/es6/destructuring/interfaceSpread.ts(7,10): error TS2339: Property 'jam' does not exist on type 'Congealed<{ jam: number; }, { peanutButter: number; }>'. -tests/cases/conformance/es6/destructuring/interfaceSpread.ts(8,10): error TS2339: Property 'peanutButter' does not exist on type 'Congealed<{ jam: number; }, { peanutButter: number; }>'. +tests/cases/conformance/types/spread/interfaceSpread.ts(2,5): error TS2699: Interface declaration cannot contain a spread property. +tests/cases/conformance/types/spread/interfaceSpread.ts(7,10): error TS2339: Property 'jam' does not exist on type 'Congealed<{ jam: number; }, { peanutButter: number; }>'. +tests/cases/conformance/types/spread/interfaceSpread.ts(8,10): error TS2339: Property 'peanutButter' does not exist on type 'Congealed<{ jam: number; }, { peanutButter: number; }>'. -==== tests/cases/conformance/es6/destructuring/interfaceSpread.ts (3 errors) ==== +==== tests/cases/conformance/types/spread/interfaceSpread.ts (3 errors) ==== interface Congealed { ...T ~~~~ diff --git a/tests/cases/conformance/es6/destructuring/interfaceSpread.ts b/tests/cases/conformance/types/spread/interfaceSpread.ts similarity index 100% rename from tests/cases/conformance/es6/destructuring/interfaceSpread.ts rename to tests/cases/conformance/types/spread/interfaceSpread.ts From 63f8c991733ec3f4ba5819452281f91e2206b8ed Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 26 Sep 2016 10:24:05 -0700 Subject: [PATCH 007/224] Object.assign uses spread types now --- src/lib/es2015.core.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/es2015.core.d.ts b/src/lib/es2015.core.d.ts index 5d570bb086630..2373b828fdd6d 100644 --- a/src/lib/es2015.core.d.ts +++ b/src/lib/es2015.core.d.ts @@ -278,7 +278,7 @@ interface ObjectConstructor { * @param target The target object to copy to. * @param source The source object from which to copy properties. */ - assign(target: T, source: U): T & U; + assign(target: T, source: U): { ...T, ...U }; /** * Copy the values of all of the enumerable own properties from one or more source objects to a @@ -287,7 +287,7 @@ interface ObjectConstructor { * @param source1 The first source object from which to copy properties. * @param source2 The second source object from which to copy properties. */ - assign(target: T, source1: U, source2: V): T & U & V; + assign(target: T, source1: U, source2: V): { ...T, ...U, ...V }; /** * Copy the values of all of the enumerable own properties from one or more source objects to a @@ -297,7 +297,7 @@ interface ObjectConstructor { * @param source2 The second source object from which to copy properties. * @param source3 The third source object from which to copy properties. */ - assign(target: T, source1: U, source2: V, source3: W): T & U & V & W; + assign(target: T, source1: U, source2: V, source3: W): { ...T, ...U, ...V, ...W }; /** * Copy the values of all of the enumerable own properties from one or more source objects to a From a0db19749ab5316254fdfa7d3dfdaa49aee3ed7a Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 26 Sep 2016 11:50:02 -0700 Subject: [PATCH 008/224] Rename Spread[Element]Expression 1. SpreadElementExpression (existing, for arrays) -> SpreadExpression 2. SpreadElement (new for object literals) -> SpreadElementExpression --- src/compiler/binder.ts | 23 +++++++------ src/compiler/checker.ts | 38 +++++++++++----------- src/compiler/emitter.ts | 6 ++-- src/compiler/factory.ts | 6 ++-- src/compiler/parser.ts | 20 +++++------- src/compiler/transformers/destructuring.ts | 4 +-- src/compiler/transformers/es6.ts | 36 ++++++++++---------- src/compiler/transformers/experimental.ts | 4 +-- src/compiler/transformers/module/system.ts | 6 ++-- src/compiler/types.ts | 18 +++++----- src/compiler/utilities.ts | 14 ++++---- src/compiler/visitor.ts | 12 +++---- src/services/breakpoints.ts | 2 +- src/services/utilities.ts | 4 +-- 14 files changed, 96 insertions(+), 97 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index d3217fd70a850..c198b5c01ff59 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1063,8 +1063,8 @@ namespace ts { } else if (node.kind === SyntaxKind.ArrayLiteralExpression) { for (const e of (node).elements) { - if (e.kind === SyntaxKind.SpreadElementExpression) { - bindAssignmentTargetFlow((e).expression); + if (e.kind === SyntaxKind.SpreadExpression) { + bindAssignmentTargetFlow((e).expression); } else { bindDestructuringTargetFlow(e); @@ -1079,6 +1079,9 @@ namespace ts { else if (p.kind === SyntaxKind.ShorthandPropertyAssignment) { bindAssignmentTargetFlow((p).name); } + else if (p.kind === SyntaxKind.SpreadElementExpression) { + bindAssignmentTargetFlow((p).expression); + } } } } @@ -1825,7 +1828,7 @@ namespace ts { case SyntaxKind.EnumMember: return bindPropertyOrMethodOrAccessor(node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes); - case SyntaxKind.SpreadElement: + case SyntaxKind.SpreadElementExpression: case SyntaxKind.JsxSpreadAttribute: emitFlags |= NodeFlags.HasSpreadAttribute; return; @@ -2369,9 +2372,9 @@ namespace ts { const expression = node.expression; const expressionKind = expression.kind; - if (subtreeFlags & TransformFlags.ContainsSpreadElementExpression + if (subtreeFlags & TransformFlags.ContainsSpreadExpression || isSuperOrSuperProperty(expression, expressionKind)) { - // If the this node contains a SpreadElementExpression, or is a super call, then it is an ES6 + // If the this node contains a SpreadExpression, or is a super call, then it is an ES6 // node. transformFlags |= TransformFlags.AssertES6; } @@ -2957,10 +2960,10 @@ namespace ts { } break; - case SyntaxKind.SpreadElement: + case SyntaxKind.SpreadExpression: case SyntaxKind.SpreadElementExpression: // This node is ES6 or ES future syntax, but is handled by a containing node. - transformFlags |= TransformFlags.ContainsSpreadElementExpression; + transformFlags |= TransformFlags.ContainsSpreadExpression; break; case SyntaxKind.SuperKeyword: @@ -2998,7 +3001,7 @@ namespace ts { transformFlags |= TransformFlags.ContainsLexicalThis; } - if (subtreeFlags & TransformFlags.ContainsSpreadElementExpression) { + if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { // If an ObjectLiteralExpression contains a spread element, then it // is an ES experimental node. transformFlags |= TransformFlags.AssertExperimental; @@ -3009,8 +3012,8 @@ namespace ts { case SyntaxKind.ArrayLiteralExpression: case SyntaxKind.NewExpression: excludeFlags = TransformFlags.ArrayLiteralOrCallOrNewExcludes; - if (subtreeFlags & TransformFlags.ContainsSpreadElementExpression) { - // If the this node contains a SpreadElementExpression, then it is an ES6 + if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { + // If the this node contains a SpreadExpression, then it is an ES6 // node. transformFlags |= TransformFlags.AssertES6; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3578aeb5e4bb9..1f31d58b19709 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8379,7 +8379,7 @@ namespace ts { unknownType; } - function getTypeOfDestructuredSpreadElement(type: Type) { + function getTypeOfDestructuredSpreadExpression(type: Type) { return createArrayType(checkIteratedTypeOrElementType(type, /*errorNode*/ undefined, /*allowStringInput*/ false) || unknownType); } @@ -8393,8 +8393,8 @@ namespace ts { return getTypeOfDestructuredArrayElement(getAssignedType(node), indexOf(node.elements, element)); } - function getAssignedTypeOfSpreadElement(node: SpreadElementExpression): Type { - return getTypeOfDestructuredSpreadElement(getAssignedType(node.parent)); + function getAssignedTypeOfSpreadExpression(node: SpreadExpression): Type { + return getTypeOfDestructuredSpreadExpression(getAssignedType(node.parent)); } function getAssignedTypeOfPropertyAssignment(node: PropertyAssignment | ShorthandPropertyAssignment): Type { @@ -8418,8 +8418,8 @@ namespace ts { return undefinedType; case SyntaxKind.ArrayLiteralExpression: return getAssignedTypeOfArrayLiteralElement(parent, node); - case SyntaxKind.SpreadElementExpression: - return getAssignedTypeOfSpreadElement(parent); + case SyntaxKind.SpreadExpression: + return getAssignedTypeOfSpreadExpression(parent); case SyntaxKind.PropertyAssignment: return getAssignedTypeOfPropertyAssignment(parent); case SyntaxKind.ShorthandPropertyAssignment: @@ -8435,7 +8435,7 @@ namespace ts { getTypeOfDestructuredProperty(parentType, node.propertyName || node.name) : !node.dotDotDotToken ? getTypeOfDestructuredArrayElement(parentType, indexOf(pattern.elements, node)) : - getTypeOfDestructuredSpreadElement(parentType); + getTypeOfDestructuredSpreadExpression(parentType); return getTypeWithDefault(type, node.initializer); } @@ -10221,7 +10221,7 @@ namespace ts { return mapper && mapper.context; } - function checkSpreadElementExpression(node: SpreadElementExpression, contextualMapper?: TypeMapper): Type { + function checkSpreadExpression(node: SpreadExpression, contextualMapper?: TypeMapper): Type { // It is usually not safe to call checkExpressionCached if we can be contextually typing. // You can tell that we are contextually typing because of the contextualMapper parameter. // While it is true that a spread element can have a contextual type, it does not do anything @@ -10243,7 +10243,7 @@ namespace ts { const elementTypes: Type[] = []; const inDestructuringPattern = isAssignmentTarget(node); for (const e of elements) { - if (inDestructuringPattern && e.kind === SyntaxKind.SpreadElementExpression) { + if (inDestructuringPattern && e.kind === SyntaxKind.SpreadExpression) { // Given the following situation: // var c: {}; // [...c] = ["", 0]; @@ -10256,7 +10256,7 @@ namespace ts { // get the contextual element type from it. So we do something similar to // getContextualTypeForElementExpression, which will crucially not error // if there is no index type / iterated type. - const restArrayType = checkExpression((e).expression, contextualMapper); + const restArrayType = checkExpression((e).expression, contextualMapper); const restElementType = getIndexTypeOfType(restArrayType, IndexKind.Number) || (languageVersion >= ScriptTarget.ES6 ? getElementTypeOfIterable(restArrayType, /*errorNode*/ undefined) : undefined); if (restElementType) { @@ -10267,7 +10267,7 @@ namespace ts { const type = checkExpressionForMutableLocation(e, contextualMapper); elementTypes.push(type); } - hasSpreadElement = hasSpreadElement || e.kind === SyntaxKind.SpreadElementExpression; + hasSpreadElement = hasSpreadElement || e.kind === SyntaxKind.SpreadExpression; } if (!hasSpreadElement) { // If array literal is actually a destructuring pattern, mark it as an implied type. We do this such @@ -10456,7 +10456,7 @@ namespace ts { prop.target = member; member = prop; } - else if (memberDecl.kind === SyntaxKind.SpreadElement) { + else if (memberDecl.kind === SyntaxKind.SpreadElementExpression) { if (propertiesArray.length > 0) { const t = createObjectLiteralType(node, hasComputedStringProperty, hasComputedNumberProperty, propertiesArray, propertiesTable, typeFlags, patternWithComputedProperties, inDestructuringPattern) as SpreadElementType; t.isDeclaredProperty = true; @@ -10466,7 +10466,7 @@ namespace ts { hasComputedStringProperty = false; hasComputedNumberProperty = false; } - spreads.push(checkExpression((memberDecl as SpreadElement).target) as SpreadElementType); + spreads.push(checkExpression((memberDecl as SpreadElementExpression).expression) as SpreadElementType); continue; } else { @@ -11503,7 +11503,7 @@ namespace ts { function getSpreadArgumentIndex(args: Expression[]): number { for (let i = 0; i < args.length; i++) { const arg = args[i]; - if (arg && arg.kind === SyntaxKind.SpreadElementExpression) { + if (arg && arg.kind === SyntaxKind.SpreadExpression) { return i; } } @@ -13474,7 +13474,7 @@ namespace ts { const elements = node.elements; const element = elements[elementIndex]; if (element.kind !== SyntaxKind.OmittedExpression) { - if (element.kind !== SyntaxKind.SpreadElementExpression) { + if (element.kind !== SyntaxKind.SpreadExpression) { const propName = "" + elementIndex; const type = isTypeAny(sourceType) ? sourceType @@ -13501,7 +13501,7 @@ namespace ts { error(element, Diagnostics.A_rest_element_must_be_last_in_an_array_destructuring_pattern); } else { - const restExpression = (element).expression; + const restExpression = (element).expression; if (restExpression.kind === SyntaxKind.BinaryExpression && (restExpression).operatorToken.kind === SyntaxKind.EqualsToken) { error((restExpression).operatorToken, Diagnostics.A_rest_element_cannot_have_an_initializer); } @@ -14137,8 +14137,8 @@ namespace ts { return checkBinaryExpression(node, contextualMapper); case SyntaxKind.ConditionalExpression: return checkConditionalExpression(node, contextualMapper); - case SyntaxKind.SpreadElementExpression: - return checkSpreadElementExpression(node, contextualMapper); + case SyntaxKind.SpreadExpression: + return checkSpreadExpression(node, contextualMapper); case SyntaxKind.OmittedExpression: return undefinedWideningType; case SyntaxKind.YieldExpression: @@ -20048,8 +20048,8 @@ namespace ts { const GetOrSetAccessor = GetAccessor | SetAccessor; for (const prop of node.properties) { - if (prop.kind === SyntaxKind.SpreadElement) { - const target = (prop as SpreadElement).target; + if (prop.kind === SyntaxKind.SpreadElementExpression) { + const target = (prop as SpreadElementExpression).expression; switch (target.kind) { case SyntaxKind.Identifier: case SyntaxKind.PropertyAccessExpression: diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index cd00f7ad76905..cc574414beb63 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -824,8 +824,8 @@ const _super = (function (geti, seti) { return emitTemplateExpression(node); case SyntaxKind.YieldExpression: return emitYieldExpression(node); - case SyntaxKind.SpreadElementExpression: - return emitSpreadElementExpression(node); + case SyntaxKind.SpreadExpression: + return emitSpreadExpression(node); case SyntaxKind.ClassExpression: return emitClassExpression(node); case SyntaxKind.OmittedExpression: @@ -1365,7 +1365,7 @@ const _super = (function (geti, seti) { emitExpressionWithPrefix(" ", node.expression); } - function emitSpreadElementExpression(node: SpreadElementExpression) { + function emitSpreadExpression(node: SpreadExpression) { write("..."); emitExpression(node.expression); } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 16eeb86f59a73..a8ee15c12c409 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -704,12 +704,12 @@ namespace ts { } export function createSpread(expression: Expression, location?: TextRange) { - const node = createNode(SyntaxKind.SpreadElementExpression, location); + const node = createNode(SyntaxKind.SpreadExpression, location); node.expression = parenthesizeExpressionForList(expression); return node; } - export function updateSpread(node: SpreadElementExpression, expression: Expression) { + export function updateSpread(node: SpreadExpression, expression: Expression) { if (node.expression !== expression) { return updateNode(createSpread(expression, node), node); } @@ -2745,4 +2745,4 @@ namespace ts { function tryGetModuleNameFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration, host: EmitHost, resolver: EmitResolver, compilerOptions: CompilerOptions) { return tryGetModuleNameFromFile(resolver.getExternalModuleFileFromDeclaration(declaration), host, compilerOptions); } -} \ No newline at end of file +} diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index ae88b6d948b5b..e0d8790d14ccc 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -74,9 +74,8 @@ namespace ts { visitNode(cbNode, (node).questionToken) || visitNode(cbNode, (node).equalsToken) || visitNode(cbNode, (node).objectAssignmentInitializer); - case SyntaxKind.SpreadElement: - return visitNode(cbNode, (node).dotDotDotToken) || - visitNode(cbNode, (node).target); + case SyntaxKind.SpreadElementExpression: + return visitNode(cbNode, (node).expression); case SyntaxKind.SpreadTypeElement: return visitNode(cbNode, (node as SpreadTypeElement).type); case SyntaxKind.Parameter: @@ -198,8 +197,8 @@ namespace ts { visitNode(cbNode, (node).whenTrue) || visitNode(cbNode, (node).colonToken) || visitNode(cbNode, (node).whenFalse); - case SyntaxKind.SpreadElementExpression: - return visitNode(cbNode, (node).expression); + case SyntaxKind.SpreadExpression: + return visitNode(cbNode, (node).expression); case SyntaxKind.Block: case SyntaxKind.ModuleBlock: return visitNodes(cbNodes, (node).statements); @@ -4102,15 +4101,15 @@ namespace ts { return finishNode(node); } - function parseSpreadElement(): Expression { - const node = createNode(SyntaxKind.SpreadElementExpression); + function parseSpreadExpression(): Expression { + const node = createNode(SyntaxKind.SpreadExpression); parseExpected(SyntaxKind.DotDotDotToken); node.expression = parseAssignmentExpressionOrHigher(); return finishNode(node); } function parseArgumentOrArrayLiteralElement(): Expression { - return token() === SyntaxKind.DotDotDotToken ? parseSpreadElement() : + return token() === SyntaxKind.DotDotDotToken ? parseSpreadExpression() : token() === SyntaxKind.CommaToken ? createNode(SyntaxKind.OmittedExpression) : parseAssignmentExpressionOrHigher(); } @@ -4145,9 +4144,8 @@ namespace ts { const fullStart = scanner.getStartPos(); const dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken); if (dotDotDotToken) { - const spreadElement = createNode(SyntaxKind.SpreadElement, fullStart); - spreadElement.dotDotDotToken = dotDotDotToken; - spreadElement.target = parseAssignmentExpressionOrHigher(); + const spreadElement = createNode(SyntaxKind.SpreadElementExpression, fullStart); + spreadElement.expression = parseAssignmentExpressionOrHigher(); return addJSDocComment(finishNode(spreadElement)); } const decorators = parseDecorators(); diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index a66f5e21d784d..7d74e58f204ae 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -310,11 +310,11 @@ namespace ts { const e = elements[i]; if (e.kind !== SyntaxKind.OmittedExpression) { // Assignment for target = value.propName should highligh whole property, hence use e as source map node - if (e.kind !== SyntaxKind.SpreadElementExpression) { + if (e.kind !== SyntaxKind.SpreadExpression) { emitDestructuringAssignment(e, createElementAccess(value, createLiteral(i)), e); } else if (i === numElements - 1) { - emitDestructuringAssignment((e).expression, createArraySlice(value, i), e); + emitDestructuringAssignment((e).expression, createArraySlice(value, i), e); } } } diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index 114a3aea54684..7582b14e4aaf1 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -2556,7 +2556,7 @@ namespace ts { // because we contain a SpreadElementExpression. const { target, thisArg } = createCallBinding(node.expression, hoistVariableDeclaration); - if (node.transformFlags & TransformFlags.ContainsSpreadElementExpression) { + if (node.transformFlags & TransformFlags.ContainsSpreadExpression) { // [source] // f(...a, b) // x.m(...a, b) @@ -2604,7 +2604,7 @@ namespace ts { */ function visitNewExpression(node: NewExpression): LeftHandSideExpression { // We are here because we contain a SpreadElementExpression. - Debug.assert((node.transformFlags & TransformFlags.ContainsSpreadElementExpression) !== 0); + Debug.assert((node.transformFlags & TransformFlags.ContainsSpreadExpression) !== 0); // [source] // new C(...a) @@ -2625,7 +2625,7 @@ namespace ts { } /** - * Transforms an array of Expression nodes that contains a SpreadElementExpression. + * Transforms an array of Expression nodes that contains a SpreadExpression. * * @param elements The array of Expression nodes. * @param needsUniqueCopy A value indicating whether to ensure that the result is a fresh array. @@ -2642,14 +2642,14 @@ namespace ts { // expressions into an array literal. const numElements = elements.length; const segments = flatten( - spanMap(elements, partitionSpreadElement, (partition, visitPartition, start, end) => + spanMap(elements, partitionSpread, (partition, visitPartition, start, end) => visitPartition(partition, multiLine, hasTrailingComma && end === numElements) ) ); if (segments.length === 1) { const firstElement = elements[0]; - return needsUniqueCopy && isSpreadElementExpression(firstElement) && firstElement.expression.kind !== SyntaxKind.ArrayLiteralExpression + return needsUniqueCopy && isSpreadExpression(firstElement) && firstElement.expression.kind !== SyntaxKind.ArrayLiteralExpression ? createArraySlice(segments[0]) : segments[0]; } @@ -2658,17 +2658,17 @@ namespace ts { return createArrayConcat(segments.shift(), segments); } - function partitionSpreadElement(node: Expression) { - return isSpreadElementExpression(node) - ? visitSpanOfSpreadElements - : visitSpanOfNonSpreadElements; + function partitionSpread(node: Expression) { + return isSpreadExpression(node) + ? visitSpanOfSpreads + : visitSpanOfNonSpreads; } - function visitSpanOfSpreadElements(chunk: Expression[], multiLine: boolean, hasTrailingComma: boolean): VisitResult { - return map(chunk, visitExpressionOfSpreadElement); + function visitSpanOfSpreads(chunk: Expression[], multiLine: boolean, hasTrailingComma: boolean): VisitResult { + return map(chunk, visitExpressionOfSpread); } - function visitSpanOfNonSpreadElements(chunk: Expression[], multiLine: boolean, hasTrailingComma: boolean): VisitResult { + function visitSpanOfNonSpreads(chunk: Expression[], multiLine: boolean, hasTrailingComma: boolean): VisitResult { return createArrayLiteral( visitNodes(createNodeArray(chunk, /*location*/ undefined, hasTrailingComma), visitor, isExpression), /*location*/ undefined, @@ -2677,11 +2677,11 @@ namespace ts { } /** - * Transforms the expression of a SpreadElementExpression node. + * Transforms the expression of a SpreadExpression node. * - * @param node A SpreadElementExpression node. + * @param node A SpreadExpression node. */ - function visitExpressionOfSpreadElement(node: SpreadElementExpression) { + function visitExpressionOfSpread(node: SpreadExpression) { return visitNode(node.expression, visitor, isExpression); } @@ -3096,12 +3096,12 @@ namespace ts { } const callArgument = singleOrUndefined((statementExpression).arguments); - if (!callArgument || !nodeIsSynthesized(callArgument) || callArgument.kind !== SyntaxKind.SpreadElementExpression) { + if (!callArgument || !nodeIsSynthesized(callArgument) || callArgument.kind !== SyntaxKind.SpreadExpression) { return false; } - const expression = (callArgument).expression; + const expression = (callArgument).expression; return isIdentifier(expression) && expression === parameter.name; } } -} \ No newline at end of file +} diff --git a/src/compiler/transformers/experimental.ts b/src/compiler/transformers/experimental.ts index 715cf91afd3e6..85af37709c744 100644 --- a/src/compiler/transformers/experimental.ts +++ b/src/compiler/transformers/experimental.ts @@ -36,12 +36,12 @@ namespace ts { let chunkObject: (ShorthandPropertyAssignment | PropertyAssignment)[]; const objects: Expression[] = []; for (const e of elements) { - if (e.kind === SyntaxKind.SpreadElement) { + if (e.kind === SyntaxKind.SpreadElementExpression) { if (chunkObject) { objects.push(createObjectLiteral(chunkObject)); chunkObject = undefined; } - const target = (e as SpreadElement).target; + const target = (e as SpreadElementExpression).expression; objects.push(visitNode(target, visitor, isExpression)); } else { diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 7b013e18e7dc0..de1522a015085 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -1120,7 +1120,7 @@ namespace ts { } function hasExportedReferenceInArrayDestructuringElement(node: Expression): boolean { - if (isSpreadElementExpression(node)) { + if (isSpreadExpression(node)) { const expression = node.expression; return isIdentifier(expression) && isExportedBinding(expression); } @@ -1139,7 +1139,7 @@ namespace ts { else if (isIdentifier(node)) { return isExportedBinding(node); } - else if (isSpreadElementExpression(node)) { + else if (isSpreadExpression(node)) { const expression = node.expression; return isIdentifier(expression) && isExportedBinding(expression); } @@ -1400,4 +1400,4 @@ namespace ts { return updated; } } -} \ No newline at end of file +} diff --git a/src/compiler/types.ts b/src/compiler/types.ts index cdf42ebea4ffc..bfadea3179420 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -241,7 +241,7 @@ namespace ts { ConditionalExpression, TemplateExpression, YieldExpression, - SpreadElementExpression, + SpreadExpression, ClassExpression, OmittedExpression, ExpressionWithTypeArguments, @@ -316,8 +316,8 @@ namespace ts { // Property assignments PropertyAssignment, ShorthandPropertyAssignment, - SpreadElement, // maybe name it SpreadProperty? - SpreadTypeElement, // maybe name it SpreadTypeNode? + SpreadElementExpression, + SpreadTypeElement, // Enum @@ -674,9 +674,7 @@ namespace ts { } // @kind(SyntaxKind.SpreadElementExpression) - export interface SpreadElement extends ObjectLiteralElement { - dotDotDotToken: Node; - target: Expression; + export interface SpreadElementExpression extends ObjectLiteralElement, SpreadExpression { } // SyntaxKind.VariableDeclaration @@ -1056,8 +1054,8 @@ namespace ts { multiLine?: boolean; } - // @kind(SyntaxKind.SpreadElementExpression) - export interface SpreadElementExpression extends Expression { + // @kind(SyntaxKind.SpreadExpression) + export interface SpreadExpression extends Expression { expression: Expression; } @@ -3140,7 +3138,7 @@ namespace ts { ContainsLexicalThisInComputedPropertyName = 1 << 17, ContainsDefaultValueAssignments = 1 << 18, ContainsParameterPropertyAssignments = 1 << 19, - ContainsSpreadElementExpression = 1 << 20, + ContainsSpreadExpression = 1 << 20, ContainsComputedPropertyName = 1 << 21, ContainsBlockScopedBinding = 1 << 22, ContainsBindingPattern = 1 << 23, @@ -3170,7 +3168,7 @@ namespace ts { ModuleExcludes = NodeExcludes | ContainsDecorators | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsHoistedDeclarationOrCompletion, TypeExcludes = ~ContainsTypeScript, ObjectLiteralExcludes = NodeExcludes | ContainsDecorators | ContainsComputedPropertyName | ContainsLexicalThisInComputedPropertyName, - ArrayLiteralOrCallOrNewExcludes = NodeExcludes | ContainsSpreadElementExpression, + ArrayLiteralOrCallOrNewExcludes = NodeExcludes | ContainsSpreadExpression, VariableDeclarationListExcludes = NodeExcludes | ContainsBindingPattern, ParameterExcludes = NodeExcludes | ContainsBindingPattern, diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 67ea182bf9868..30a09ade0f596 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1178,7 +1178,7 @@ namespace ts { case SyntaxKind.PostfixUnaryExpression: case SyntaxKind.BinaryExpression: case SyntaxKind.ConditionalExpression: - case SyntaxKind.SpreadElementExpression: + case SyntaxKind.SpreadExpression: case SyntaxKind.TemplateExpression: case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.OmittedExpression: @@ -1633,7 +1633,7 @@ namespace ts { } while (true) { const parent = node.parent; - if (parent.kind === SyntaxKind.ArrayLiteralExpression || parent.kind === SyntaxKind.SpreadElementExpression) { + if (parent.kind === SyntaxKind.ArrayLiteralExpression || parent.kind === SyntaxKind.SpreadExpression) { node = parent; continue; } @@ -2209,7 +2209,7 @@ namespace ts { case SyntaxKind.YieldExpression: return 2; - case SyntaxKind.SpreadElementExpression: + case SyntaxKind.SpreadExpression: return 1; default: @@ -3740,7 +3740,7 @@ namespace ts { const kind = node.kind; return kind === SyntaxKind.PropertyAssignment || kind === SyntaxKind.ShorthandPropertyAssignment - || kind === SyntaxKind.SpreadElement + || kind === SyntaxKind.SpreadElementExpression || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.GetAccessor || kind === SyntaxKind.SetAccessor @@ -3820,8 +3820,8 @@ namespace ts { || kind === SyntaxKind.NoSubstitutionTemplateLiteral; } - export function isSpreadElementExpression(node: Node): node is SpreadElementExpression { - return node.kind === SyntaxKind.SpreadElementExpression; + export function isSpreadExpression(node: Node): node is SpreadExpression { + return node.kind === SyntaxKind.SpreadExpression; } export function isExpressionWithTypeArguments(node: Node): node is ExpressionWithTypeArguments { @@ -3879,7 +3879,7 @@ namespace ts { || kind === SyntaxKind.YieldExpression || kind === SyntaxKind.ArrowFunction || kind === SyntaxKind.BinaryExpression - || kind === SyntaxKind.SpreadElementExpression + || kind === SyntaxKind.SpreadExpression || kind === SyntaxKind.AsExpression || kind === SyntaxKind.OmittedExpression || isUnaryExpressionKind(kind); diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 1e0cadaaa6b99..691e13b03d69c 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -267,9 +267,9 @@ namespace ts { case SyntaxKind.VoidExpression: case SyntaxKind.AwaitExpression: case SyntaxKind.YieldExpression: - case SyntaxKind.SpreadElementExpression: + case SyntaxKind.SpreadExpression: case SyntaxKind.NonNullExpression: - result = reduceNode((node).expression, f, result); + result = reduceNode((node).expression, f, result); break; case SyntaxKind.PrefixUnaryExpression: @@ -869,9 +869,9 @@ namespace ts { return updateYield(node, visitNode((node).expression, visitor, isExpression)); - case SyntaxKind.SpreadElementExpression: - return updateSpread(node, - visitNode((node).expression, visitor, isExpression)); + case SyntaxKind.SpreadExpression: + return updateSpread(node, + visitNode((node).expression, visitor, isExpression)); case SyntaxKind.ClassExpression: return updateClassExpression(node, @@ -1357,4 +1357,4 @@ namespace ts { } } } -} \ No newline at end of file +} diff --git a/src/services/breakpoints.ts b/src/services/breakpoints.ts index bf7fb981daaca..f1cc233e67680 100644 --- a/src/services/breakpoints.ts +++ b/src/services/breakpoints.ts @@ -264,7 +264,7 @@ namespace ts.BreakpointResolver { // a or ...c or d: x from // [a, b, ...c] or { a, b } or { d: x } from destructuring pattern if ((node.kind === SyntaxKind.Identifier || - node.kind == SyntaxKind.SpreadElementExpression || + node.kind == SyntaxKind.SpreadExpression || node.kind === SyntaxKind.PropertyAssignment || node.kind === SyntaxKind.ShorthandPropertyAssignment) && isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent)) { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index d1f3a54563f91..e5de4437a5eca 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -530,8 +530,8 @@ namespace ts { case SyntaxKind.DeleteExpression: case SyntaxKind.VoidExpression: case SyntaxKind.YieldExpression: - case SyntaxKind.SpreadElementExpression: - const unaryWordExpression = (n); + case SyntaxKind.SpreadExpression: + const unaryWordExpression = n as (TypeOfExpression | DeleteExpression | VoidExpression | YieldExpression | SpreadExpression); return isCompletedNode(unaryWordExpression.expression, sourceFile); case SyntaxKind.TaggedTemplateExpression: From 9a7ebb0ac5eadf9630577b11eb0064c04ed6cd44 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 26 Sep 2016 11:53:11 -0700 Subject: [PATCH 009/224] Change new file to use CRLF What's YOUR favourite thing about Mars, Beartato? --- src/compiler/transformers/experimental.ts | 160 +++++++++++----------- 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/src/compiler/transformers/experimental.ts b/src/compiler/transformers/experimental.ts index 85af37709c744..2ee623db43aed 100644 --- a/src/compiler/transformers/experimental.ts +++ b/src/compiler/transformers/experimental.ts @@ -1,80 +1,80 @@ -/// -/// - -/*@internal*/ -namespace ts { - export function transformExperimental(context: TransformationContext) { - return transformSourceFile; - - function transformSourceFile(node: SourceFile) { - return visitEachChild(node, visitor, context); - } - - function visitor(node: Node): VisitResult { - if (node.transformFlags & TransformFlags.Experimental) { - return visitorWorker(node); - } - else if (node.transformFlags & TransformFlags.ContainsExperimental) { - return visitEachChild(node, visitor, context); - } - else { - return node; - } - } - - function visitorWorker(node: Node): VisitResult { - switch (node.kind) { - case SyntaxKind.ObjectLiteralExpression: - return visitObjectLiteralExpression(node as ObjectLiteralExpression); - default: - Debug.failBadSyntaxKind(node); - return visitEachChild(node, visitor, context); - } - } - - function chunkObjectLiteralElements(elements: ObjectLiteralElement[]): Expression[] { - let chunkObject: (ShorthandPropertyAssignment | PropertyAssignment)[]; - const objects: Expression[] = []; - for (const e of elements) { - if (e.kind === SyntaxKind.SpreadElementExpression) { - if (chunkObject) { - objects.push(createObjectLiteral(chunkObject)); - chunkObject = undefined; - } - const target = (e as SpreadElementExpression).expression; - objects.push(visitNode(target, visitor, isExpression)); - } - else { - if (!chunkObject) { - chunkObject = []; - } - if (e.kind === SyntaxKind.PropertyAssignment) { - const p = e as PropertyAssignment; - chunkObject.push(createPropertyAssignment(p.name, visitNode(p.initializer, visitor, isExpression))); - } - else { - chunkObject.push(e as ShorthandPropertyAssignment); - } - } - } - if (chunkObject) { - objects.push(createObjectLiteral(chunkObject)); - } - - return objects; - } - - function visitObjectLiteralExpression(node: ObjectLiteralExpression): Expression { - // spread elements emit like so: - // non-spread elements are chunked together into object literals, and then all are passed to __assign: - // { a, ...o, b } => __assign({a}, o, {b}); - // If the first element is a spread element, then the first argument to __assign is {}: - // { ...o, a, b, ...o2 } => __assign({}, o, {a, b}, o2) - const objects = chunkObjectLiteralElements(node.properties); - if (objects.length && objects[0].kind !== SyntaxKind.ObjectLiteralExpression) { - objects.unshift(createObjectLiteral()); - } - return createCall(createIdentifier("__assign"), undefined, objects); - } - } -} +/// +/// + +/*@internal*/ +namespace ts { + export function transformExperimental(context: TransformationContext) { + return transformSourceFile; + + function transformSourceFile(node: SourceFile) { + return visitEachChild(node, visitor, context); + } + + function visitor(node: Node): VisitResult { + if (node.transformFlags & TransformFlags.Experimental) { + return visitorWorker(node); + } + else if (node.transformFlags & TransformFlags.ContainsExperimental) { + return visitEachChild(node, visitor, context); + } + else { + return node; + } + } + + function visitorWorker(node: Node): VisitResult { + switch (node.kind) { + case SyntaxKind.ObjectLiteralExpression: + return visitObjectLiteralExpression(node as ObjectLiteralExpression); + default: + Debug.failBadSyntaxKind(node); + return visitEachChild(node, visitor, context); + } + } + + function chunkObjectLiteralElements(elements: ObjectLiteralElement[]): Expression[] { + let chunkObject: (ShorthandPropertyAssignment | PropertyAssignment)[]; + const objects: Expression[] = []; + for (const e of elements) { + if (e.kind === SyntaxKind.SpreadElementExpression) { + if (chunkObject) { + objects.push(createObjectLiteral(chunkObject)); + chunkObject = undefined; + } + const target = (e as SpreadElementExpression).expression; + objects.push(visitNode(target, visitor, isExpression)); + } + else { + if (!chunkObject) { + chunkObject = []; + } + if (e.kind === SyntaxKind.PropertyAssignment) { + const p = e as PropertyAssignment; + chunkObject.push(createPropertyAssignment(p.name, visitNode(p.initializer, visitor, isExpression))); + } + else { + chunkObject.push(e as ShorthandPropertyAssignment); + } + } + } + if (chunkObject) { + objects.push(createObjectLiteral(chunkObject)); + } + + return objects; + } + + function visitObjectLiteralExpression(node: ObjectLiteralExpression): Expression { + // spread elements emit like so: + // non-spread elements are chunked together into object literals, and then all are passed to __assign: + // { a, ...o, b } => __assign({a}, o, {b}); + // If the first element is a spread element, then the first argument to __assign is {}: + // { ...o, a, b, ...o2 } => __assign({}, o, {a, b}, o2) + const objects = chunkObjectLiteralElements(node.properties); + if (objects.length && objects[0].kind !== SyntaxKind.ObjectLiteralExpression) { + objects.unshift(createObjectLiteral()); + } + return createCall(createIdentifier("__assign"), undefined, objects); + } + } +} From cfdf75176863a08fd9112649d9c5a34acd7e6505 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 27 Sep 2016 10:30:13 -0700 Subject: [PATCH 010/224] Make index signatures work on spread types Previously, they worked when they came from a spread type but not when written in the object literal itself. --- src/compiler/checker.ts | 59 ++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1f31d58b19709..66a5d9fc4153d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2321,6 +2321,9 @@ namespace ts { printFollowingPunctuation = true; } } + const resolved = resolveStructuredTypeMembers(type); + writeIndexSignature(resolved.stringIndexInfo, SyntaxKind.StringKeyword); + writeIndexSignature(resolved.numberIndexInfo, SyntaxKind.NumberKeyword); writer.decreaseIndent(); if (printFollowingPunctuation) { writeSpace(writer); @@ -4306,15 +4309,14 @@ namespace ts { function resolveSpreadTypeMembers(type: SpreadType) { // The members and properties collections are empty for spread types. To get all properties of an - // spread type use getPropertiesOfType (only the language service uses this). - let stringIndexInfo: IndexInfo = undefined; - let numberIndexInfo: IndexInfo = undefined; + // spread type use getPropertiesOfType. + let stringIndexInfo: IndexInfo = getIndexInfoOfSymbol(type.symbol, IndexKind.String); + let numberIndexInfo: IndexInfo = getIndexInfoOfSymbol(type.symbol, IndexKind.Number); for (let i = type.types.length - 1; i > -1; i--) { const t = type.types[i]; - stringIndexInfo = intersectIndexInfos(stringIndexInfo, getIndexInfoOfType(t, IndexKind.String)); - numberIndexInfo = intersectIndexInfos(numberIndexInfo, getIndexInfoOfType(t, IndexKind.Number)); - if (!t.symbol || !(t.symbol.flags & SymbolFlags.Optional)) { - break; + if (!t.isDeclaredProperty) { + stringIndexInfo = intersectIndexInfos(stringIndexInfo, getIndexInfoOfType(t, IndexKind.String)); + numberIndexInfo = intersectIndexInfos(numberIndexInfo, getIndexInfoOfType(t, IndexKind.Number)); } } setObjectTypeMembers(type, emptySymbols, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); @@ -4545,6 +4547,9 @@ namespace ts { result.containingType = containingType; result.hasCommonType = hasCommonType; result.declarations = declarations; + if (declarations.length) { + result.valueDeclaration = declarations[0]; + } result.isReadonly = isReadonly; result.type = containingType.flags & TypeFlags.Intersection ? getIntersectionType(propTypes) : getUnionType(propTypes); return result; @@ -5059,7 +5064,7 @@ namespace ts { const declaration = getIndexDeclarationOfSymbol(symbol, kind); if (declaration) { return createIndexInfo(declaration.type ? getTypeFromTypeNode(declaration.type) : anyType, - (getModifierFlags(declaration) & ModifierFlags.Readonly) !== 0, declaration); + (getModifierFlags(declaration) & ModifierFlags.Readonly) !== 0, declaration); } return undefined; } @@ -5683,11 +5688,15 @@ namespace ts { } spreads.push(getTypeFromTypeNode((member as SpreadTypeElement).type) as SpreadElementType); } - else if (member.kind !== SyntaxKind.CallSignature && member.kind !== SyntaxKind.ConstructSignature) { - // note that spread types don't include call and construct signatures + else if (member.kind !== SyntaxKind.CallSignature && + member.kind !== SyntaxKind.ConstructSignature && + member.kind !== SyntaxKind.IndexSignature) { + // note that spread types don't include call and construct signatures, and index signatures are resolved later const flags = SymbolFlags.Property | SymbolFlags.Transient | (member.questionToken ? SymbolFlags.Optional : 0); const text = getTextOfPropertyName(member.name); const symbol = createSymbol(flags, text); + symbol.declarations = [member]; + symbol.valueDeclaration = member; symbol.type = getTypeFromTypeNodeNoAlias((member as IndexSignatureDeclaration | PropertySignature | MethodSignature).type); if (!members) { members = createMap(); @@ -10458,7 +10467,7 @@ namespace ts { } else if (memberDecl.kind === SyntaxKind.SpreadElementExpression) { if (propertiesArray.length > 0) { - const t = createObjectLiteralType(node, hasComputedStringProperty, hasComputedNumberProperty, propertiesArray, propertiesTable, typeFlags, patternWithComputedProperties, inDestructuringPattern) as SpreadElementType; + const t = createObjectLiteralType() as SpreadElementType; t.isDeclaredProperty = true; spreads.push(t); propertiesArray = []; @@ -10510,7 +10519,7 @@ namespace ts { if (spreads.length > 0) { if (propertiesArray.length > 0) { - const t = createObjectLiteralType(node, hasComputedStringProperty, hasComputedNumberProperty, propertiesArray, propertiesTable, typeFlags, patternWithComputedProperties, inDestructuringPattern) as SpreadElementType; + const t = createObjectLiteralType() as SpreadElementType; t.isDeclaredProperty = true; spreads.push(t); } @@ -10520,20 +10529,20 @@ namespace ts { return spread; } - return createObjectLiteralType(node, hasComputedStringProperty, hasComputedNumberProperty, propertiesArray, propertiesTable, typeFlags, patternWithComputedProperties, inDestructuringPattern); - } - - function createObjectLiteralType(node: ObjectLiteralExpression, hasComputedStringProperty: boolean, hasComputedNumberProperty: boolean, propertiesArray: Symbol[], propertiesTable: Map, typeFlags: TypeFlags, patternWithComputedProperties: boolean, inDestructuringPattern: boolean) { - const stringIndexInfo = hasComputedStringProperty ? getObjectLiteralIndexInfo(node, propertiesArray, IndexKind.String) : undefined; - const numberIndexInfo = hasComputedNumberProperty ? getObjectLiteralIndexInfo(node, propertiesArray, IndexKind.Number) : undefined; - const result = createAnonymousType(node.symbol, propertiesTable, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); - const freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : TypeFlags.FreshObjectLiteral; - result.flags |= TypeFlags.ObjectLiteral | TypeFlags.ContainsObjectLiteral | freshObjectLiteralFlag | (typeFlags & TypeFlags.PropagatingFlags) | (patternWithComputedProperties ? TypeFlags.ObjectLiteralPatternWithComputedProperties : 0); - if (inDestructuringPattern) { - result.pattern = node; + return createObjectLiteralType(); + function createObjectLiteralType() { + const stringIndexInfo = hasComputedStringProperty ? getObjectLiteralIndexInfo(node, propertiesArray, IndexKind.String) : undefined; + const numberIndexInfo = hasComputedNumberProperty ? getObjectLiteralIndexInfo(node, propertiesArray, IndexKind.Number) : undefined; + const result = createAnonymousType(node.symbol, propertiesTable, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); + const freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : TypeFlags.FreshObjectLiteral; + result.flags |= TypeFlags.ObjectLiteral | TypeFlags.ContainsObjectLiteral | freshObjectLiteralFlag | (typeFlags & TypeFlags.PropagatingFlags) | (patternWithComputedProperties ? TypeFlags.ObjectLiteralPatternWithComputedProperties : 0); + if (inDestructuringPattern) { + result.pattern = node; + } + return result; } - return result; - } + + } function checkJsxSelfClosingElement(node: JsxSelfClosingElement) { checkJsxOpeningLikeElement(node); From d6e414ce5b3e9532dd05651854f1d2f694de6e8c Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 27 Sep 2016 10:33:50 -0700 Subject: [PATCH 011/224] Test spread type index signatures --- .../reference/objectSpreadIndexSignature.js | 29 ++++++++++ .../objectSpreadIndexSignature.symbols | 51 +++++++++++++++++ .../objectSpreadIndexSignature.types | 57 +++++++++++++++++++ .../spread/objectSpreadIndexSignature.ts | 14 +++++ 4 files changed, 151 insertions(+) create mode 100644 tests/baselines/reference/objectSpreadIndexSignature.js create mode 100644 tests/baselines/reference/objectSpreadIndexSignature.symbols create mode 100644 tests/baselines/reference/objectSpreadIndexSignature.types create mode 100644 tests/cases/conformance/types/spread/objectSpreadIndexSignature.ts diff --git a/tests/baselines/reference/objectSpreadIndexSignature.js b/tests/baselines/reference/objectSpreadIndexSignature.js new file mode 100644 index 0000000000000..c4a0d6b90d8a8 --- /dev/null +++ b/tests/baselines/reference/objectSpreadIndexSignature.js @@ -0,0 +1,29 @@ +//// [objectSpreadIndexSignature.ts] +class C { + a: number; + c: boolean; +} +let c: { ...C, b: string, c?: string, [n: number]: string }; +let n: number = c.a; +let s: string = c[12]; +interface Indexed { + [n: number]: string; + a: boolean; +} +let i: { ...Indexed, b: string }; +s = i[101]; +s = i.b; + + +//// [objectSpreadIndexSignature.js] +var C = (function () { + function C() { + } + return C; +}()); +var c; +var n = c.a; +var s = c[12]; +var i; +s = i[101]; +s = i.b; diff --git a/tests/baselines/reference/objectSpreadIndexSignature.symbols b/tests/baselines/reference/objectSpreadIndexSignature.symbols new file mode 100644 index 0000000000000..fee1e862adea6 --- /dev/null +++ b/tests/baselines/reference/objectSpreadIndexSignature.symbols @@ -0,0 +1,51 @@ +=== tests/cases/conformance/types/spread/objectSpreadIndexSignature.ts === +class C { +>C : Symbol(C, Decl(objectSpreadIndexSignature.ts, 0, 0)) + + a: number; +>a : Symbol(C.a, Decl(objectSpreadIndexSignature.ts, 0, 9)) + + c: boolean; +>c : Symbol(C.c, Decl(objectSpreadIndexSignature.ts, 1, 14)) +} +let c: { ...C, b: string, c?: string, [n: number]: string }; +>c : Symbol(c, Decl(objectSpreadIndexSignature.ts, 4, 3)) +>C : Symbol(C, Decl(objectSpreadIndexSignature.ts, 0, 0)) +>b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 4, 14)) +>c : Symbol(c, Decl(objectSpreadIndexSignature.ts, 4, 25)) +>n : Symbol(n, Decl(objectSpreadIndexSignature.ts, 4, 39)) + +let n: number = c.a; +>n : Symbol(n, Decl(objectSpreadIndexSignature.ts, 5, 3)) +>c.a : Symbol(C.a, Decl(objectSpreadIndexSignature.ts, 0, 9)) +>c : Symbol(c, Decl(objectSpreadIndexSignature.ts, 4, 3)) +>a : Symbol(C.a, Decl(objectSpreadIndexSignature.ts, 0, 9)) + +let s: string = c[12]; +>s : Symbol(s, Decl(objectSpreadIndexSignature.ts, 6, 3)) +>c : Symbol(c, Decl(objectSpreadIndexSignature.ts, 4, 3)) + +interface Indexed { +>Indexed : Symbol(Indexed, Decl(objectSpreadIndexSignature.ts, 6, 22)) + + [n: number]: string; +>n : Symbol(n, Decl(objectSpreadIndexSignature.ts, 8, 5)) + + a: boolean; +>a : Symbol(Indexed.a, Decl(objectSpreadIndexSignature.ts, 8, 24)) +} +let i: { ...Indexed, b: string }; +>i : Symbol(i, Decl(objectSpreadIndexSignature.ts, 11, 3)) +>Indexed : Symbol(Indexed, Decl(objectSpreadIndexSignature.ts, 6, 22)) +>b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 11, 20)) + +s = i[101]; +>s : Symbol(s, Decl(objectSpreadIndexSignature.ts, 6, 3)) +>i : Symbol(i, Decl(objectSpreadIndexSignature.ts, 11, 3)) + +s = i.b; +>s : Symbol(s, Decl(objectSpreadIndexSignature.ts, 6, 3)) +>i.b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 11, 20)) +>i : Symbol(i, Decl(objectSpreadIndexSignature.ts, 11, 3)) +>b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 11, 20)) + diff --git a/tests/baselines/reference/objectSpreadIndexSignature.types b/tests/baselines/reference/objectSpreadIndexSignature.types new file mode 100644 index 0000000000000..9adbb349da2d2 --- /dev/null +++ b/tests/baselines/reference/objectSpreadIndexSignature.types @@ -0,0 +1,57 @@ +=== tests/cases/conformance/types/spread/objectSpreadIndexSignature.ts === +class C { +>C : C + + a: number; +>a : number + + c: boolean; +>c : boolean +} +let c: { ...C, b: string, c?: string, [n: number]: string }; +>c : { ...C; b: string; c?: string; [n: number]: string; } +>C : C +>b : string +>c : string +>n : number + +let n: number = c.a; +>n : number +>c.a : number +>c : { ...C; b: string; c?: string; [n: number]: string; } +>a : number + +let s: string = c[12]; +>s : string +>c[12] : string +>c : { ...C; b: string; c?: string; [n: number]: string; } +>12 : 12 + +interface Indexed { +>Indexed : Indexed + + [n: number]: string; +>n : number + + a: boolean; +>a : boolean +} +let i: { ...Indexed, b: string }; +>i : { ...Indexed; b: string; [n: number]: string; } +>Indexed : Indexed +>b : string + +s = i[101]; +>s = i[101] : string +>s : string +>i[101] : string +>i : { ...Indexed; b: string; [n: number]: string; } +>101 : 101 + +s = i.b; +>s = i.b : string +>s : string +>i.b : string +>i : { ...Indexed; b: string; [n: number]: string; } +>b : string + diff --git a/tests/cases/conformance/types/spread/objectSpreadIndexSignature.ts b/tests/cases/conformance/types/spread/objectSpreadIndexSignature.ts new file mode 100644 index 0000000000000..11ba545008504 --- /dev/null +++ b/tests/cases/conformance/types/spread/objectSpreadIndexSignature.ts @@ -0,0 +1,14 @@ +class C { + a: number; + c: boolean; +} +let c: { ...C, b: string, c?: string, [n: number]: string }; +let n: number = c.a; +let s: string = c[12]; +interface Indexed { + [n: number]: string; + a: boolean; +} +let i: { ...Indexed, b: string }; +s = i[101]; +s = i.b; From 429b0d95ca54538cabdd09e59eeca71661fe72d9 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 27 Sep 2016 11:05:12 -0700 Subject: [PATCH 012/224] Union multiple spread index signatures --- src/compiler/checker.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 66a5d9fc4153d..e1e7cce4df41c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2321,6 +2321,8 @@ namespace ts { printFollowingPunctuation = true; } } + // TODO: Only print if this is directly on the type -- not on the subtype somewhere. + // (This is not crucial, though, since the extra info *might* be nice.) const resolved = resolveStructuredTypeMembers(type); writeIndexSignature(resolved.stringIndexInfo, SyntaxKind.StringKeyword); writeIndexSignature(resolved.numberIndexInfo, SyntaxKind.NumberKeyword); @@ -4291,6 +4293,11 @@ namespace ts { getIntersectionType([info1.type, info2.type]), info1.isReadonly && info2.isReadonly); } + function unionIndexInfos(info1: IndexInfo, info2: IndexInfo): IndexInfo { + return !info1 ? info2 : !info2 ? info1 : createIndexInfo( + getUnionType([info1.type, info2.type]), info1.isReadonly || info2.isReadonly); + } + function resolveIntersectionTypeMembers(type: IntersectionType) { // The members and properties collections are empty for intersection types. To get all properties of an // intersection type use getPropertiesOfType (only the language service uses this). @@ -4315,8 +4322,8 @@ namespace ts { for (let i = type.types.length - 1; i > -1; i--) { const t = type.types[i]; if (!t.isDeclaredProperty) { - stringIndexInfo = intersectIndexInfos(stringIndexInfo, getIndexInfoOfType(t, IndexKind.String)); - numberIndexInfo = intersectIndexInfos(numberIndexInfo, getIndexInfoOfType(t, IndexKind.Number)); + stringIndexInfo = unionIndexInfos(stringIndexInfo, getIndexInfoOfType(t, IndexKind.String)); + numberIndexInfo = unionIndexInfos(numberIndexInfo, getIndexInfoOfType(t, IndexKind.Number)); } } setObjectTypeMembers(type, emptySymbols, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); @@ -16839,7 +16846,9 @@ namespace ts { // perform property check if property or indexer is declared in 'type' // this allows to rule out cases when both property and indexer are inherited from the base class let errorNode: Node; - if (prop.valueDeclaration.name.kind === SyntaxKind.ComputedPropertyName || prop.parent === containingType.symbol) { + if (prop.valueDeclaration.name.kind === SyntaxKind.ComputedPropertyName || + prop.parent === containingType.symbol || + containingType.flags & TypeFlags.Spread) { errorNode = prop.valueDeclaration; } else if (indexDeclaration) { From 78420adfcdf67a9e79025edee9a33e886c72ba94 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 27 Sep 2016 11:05:58 -0700 Subject: [PATCH 013/224] Test object spread index signatures --- .../reference/objectSpreadIndexSignature.js | 22 ++++--- .../objectSpreadIndexSignature.symbols | 34 ++++++++--- .../objectSpreadIndexSignature.types | 58 +++++++++++++------ .../spread/objectSpreadIndexSignature.ts | 16 +++-- 4 files changed, 93 insertions(+), 37 deletions(-) diff --git a/tests/baselines/reference/objectSpreadIndexSignature.js b/tests/baselines/reference/objectSpreadIndexSignature.js index c4a0d6b90d8a8..b5ec35277e0c3 100644 --- a/tests/baselines/reference/objectSpreadIndexSignature.js +++ b/tests/baselines/reference/objectSpreadIndexSignature.js @@ -7,12 +7,18 @@ let c: { ...C, b: string, c?: string, [n: number]: string }; let n: number = c.a; let s: string = c[12]; interface Indexed { - [n: number]: string; - a: boolean; + [n: string]: number; + a: number; +} +let i: { ...Indexed, b: number }; +n = i[101]; +n = i.b; +interface Indexed2 { + [n: string]: boolean; + c: boolean; } -let i: { ...Indexed, b: string }; -s = i[101]; -s = i.b; +let ii: { ...Indexed, ...Indexed2, b: boolean, d: number }; +let nb: number | boolean = ii[1001]; //// [objectSpreadIndexSignature.js] @@ -25,5 +31,7 @@ var c; var n = c.a; var s = c[12]; var i; -s = i[101]; -s = i.b; +n = i[101]; +n = i.b; +var ii; +var nb = ii[1001]; diff --git a/tests/baselines/reference/objectSpreadIndexSignature.symbols b/tests/baselines/reference/objectSpreadIndexSignature.symbols index fee1e862adea6..a3510f9865830 100644 --- a/tests/baselines/reference/objectSpreadIndexSignature.symbols +++ b/tests/baselines/reference/objectSpreadIndexSignature.symbols @@ -28,24 +28,44 @@ let s: string = c[12]; interface Indexed { >Indexed : Symbol(Indexed, Decl(objectSpreadIndexSignature.ts, 6, 22)) - [n: number]: string; + [n: string]: number; >n : Symbol(n, Decl(objectSpreadIndexSignature.ts, 8, 5)) - a: boolean; + a: number; >a : Symbol(Indexed.a, Decl(objectSpreadIndexSignature.ts, 8, 24)) } -let i: { ...Indexed, b: string }; +let i: { ...Indexed, b: number }; >i : Symbol(i, Decl(objectSpreadIndexSignature.ts, 11, 3)) >Indexed : Symbol(Indexed, Decl(objectSpreadIndexSignature.ts, 6, 22)) >b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 11, 20)) -s = i[101]; ->s : Symbol(s, Decl(objectSpreadIndexSignature.ts, 6, 3)) +n = i[101]; +>n : Symbol(n, Decl(objectSpreadIndexSignature.ts, 5, 3)) >i : Symbol(i, Decl(objectSpreadIndexSignature.ts, 11, 3)) -s = i.b; ->s : Symbol(s, Decl(objectSpreadIndexSignature.ts, 6, 3)) +n = i.b; +>n : Symbol(n, Decl(objectSpreadIndexSignature.ts, 5, 3)) >i.b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 11, 20)) >i : Symbol(i, Decl(objectSpreadIndexSignature.ts, 11, 3)) >b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 11, 20)) +interface Indexed2 { +>Indexed2 : Symbol(Indexed2, Decl(objectSpreadIndexSignature.ts, 13, 8)) + + [n: string]: boolean; +>n : Symbol(n, Decl(objectSpreadIndexSignature.ts, 15, 5)) + + c: boolean; +>c : Symbol(Indexed2.c, Decl(objectSpreadIndexSignature.ts, 15, 25)) +} +let ii: { ...Indexed, ...Indexed2, b: boolean, d: number }; +>ii : Symbol(ii, Decl(objectSpreadIndexSignature.ts, 18, 3)) +>Indexed : Symbol(Indexed, Decl(objectSpreadIndexSignature.ts, 6, 22)) +>Indexed2 : Symbol(Indexed2, Decl(objectSpreadIndexSignature.ts, 13, 8)) +>b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 18, 34)) +>d : Symbol(d, Decl(objectSpreadIndexSignature.ts, 18, 46)) + +let nb: number | boolean = ii[1001]; +>nb : Symbol(nb, Decl(objectSpreadIndexSignature.ts, 19, 3)) +>ii : Symbol(ii, Decl(objectSpreadIndexSignature.ts, 18, 3)) + diff --git a/tests/baselines/reference/objectSpreadIndexSignature.types b/tests/baselines/reference/objectSpreadIndexSignature.types index 9adbb349da2d2..9a31b0c44512b 100644 --- a/tests/baselines/reference/objectSpreadIndexSignature.types +++ b/tests/baselines/reference/objectSpreadIndexSignature.types @@ -30,28 +30,50 @@ let s: string = c[12]; interface Indexed { >Indexed : Indexed - [n: number]: string; ->n : number + [n: string]: number; +>n : string - a: boolean; ->a : boolean + a: number; +>a : number } -let i: { ...Indexed, b: string }; ->i : { ...Indexed; b: string; [n: number]: string; } +let i: { ...Indexed, b: number }; +>i : { ...Indexed; b: number; [n: string]: number; } >Indexed : Indexed ->b : string +>b : number -s = i[101]; ->s = i[101] : string ->s : string ->i[101] : string ->i : { ...Indexed; b: string; [n: number]: string; } +n = i[101]; +>n = i[101] : number +>n : number +>i[101] : number +>i : { ...Indexed; b: number; [n: string]: number; } >101 : 101 -s = i.b; ->s = i.b : string ->s : string ->i.b : string ->i : { ...Indexed; b: string; [n: number]: string; } ->b : string +n = i.b; +>n = i.b : number +>n : number +>i.b : number +>i : { ...Indexed; b: number; [n: string]: number; } +>b : number + +interface Indexed2 { +>Indexed2 : Indexed2 + + [n: string]: boolean; +>n : string + + c: boolean; +>c : boolean +} +let ii: { ...Indexed, ...Indexed2, b: boolean, d: number }; +>ii : { ...Indexed; ...Indexed2; b: boolean; d: number; [x: string]: number | boolean; } +>Indexed : Indexed +>Indexed2 : Indexed2 +>b : boolean +>d : number + +let nb: number | boolean = ii[1001]; +>nb : number | boolean +>ii[1001] : number | boolean +>ii : { ...Indexed; ...Indexed2; b: boolean; d: number; [x: string]: number | boolean; } +>1001 : 1001 diff --git a/tests/cases/conformance/types/spread/objectSpreadIndexSignature.ts b/tests/cases/conformance/types/spread/objectSpreadIndexSignature.ts index 11ba545008504..d6f9f511c7b9e 100644 --- a/tests/cases/conformance/types/spread/objectSpreadIndexSignature.ts +++ b/tests/cases/conformance/types/spread/objectSpreadIndexSignature.ts @@ -6,9 +6,15 @@ let c: { ...C, b: string, c?: string, [n: number]: string }; let n: number = c.a; let s: string = c[12]; interface Indexed { - [n: number]: string; - a: boolean; + [n: string]: number; + a: number; +} +let i: { ...Indexed, b: number }; +n = i[101]; +n = i.b; +interface Indexed2 { + [n: string]: boolean; + c: boolean; } -let i: { ...Indexed, b: string }; -s = i[101]; -s = i.b; +let ii: { ...Indexed, ...Indexed2, b: boolean, d: number }; +let nb: number | boolean = ii[1001]; From 7e7a26a3581c223f57f161dbc8f71ed4ac856590 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 27 Sep 2016 16:15:52 -0700 Subject: [PATCH 014/224] Spreads w/a single type parameter assignable to that type parameter --- src/compiler/checker.ts | 47 ++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e1e7cce4df41c..209598259ebc5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2321,8 +2321,6 @@ namespace ts { printFollowingPunctuation = true; } } - // TODO: Only print if this is directly on the type -- not on the subtype somewhere. - // (This is not crucial, though, since the extra info *might* be nice.) const resolved = resolveStructuredTypeMembers(type); writeIndexSignature(resolved.stringIndexInfo, SyntaxKind.StringKeyword); writeIndexSignature(resolved.numberIndexInfo, SyntaxKind.NumberKeyword); @@ -6656,23 +6654,42 @@ namespace ts { } } - if (source.flags & TypeFlags.Spread && target.flags & TypeFlags.Spread) { - const sourceParameters = filter((source as SpreadType).types, t => !!(t.flags & TypeFlags.TypeParameter)); - const targetParameters = filter((target as SpreadType).types, t => !!(t.flags & TypeFlags.TypeParameter)); - if (sourceParameters.length !== targetParameters.length) { - reportRelationError(headMessage, source, target); - return Ternary.False; + if (source.flags & TypeFlags.Spread) { + if (target.flags & TypeFlags.TypeParameter) { + let hasTypeParameter = false; + let typeParametersAreEqual = true; + for (const t of (source as SpreadType).types) { + if (t.flags & TypeFlags.TypeParameter) { + hasTypeParameter = true; + if (t !== target) { + typeParametersAreEqual = false; + break; + } + } + } + if (hasTypeParameter && typeParametersAreEqual) { + errorInfo = saveErrorInfo; + return Ternary.True; + } } - for (let i = 0; i < sourceParameters.length; i++) { - if (sourceParameters[i].symbol !== targetParameters[i].symbol) { + else if (target.flags & TypeFlags.Spread) { + const sourceParameters = filter((source as SpreadType).types, t => !!(t.flags & TypeFlags.TypeParameter)); + const targetParameters = filter((target as SpreadType).types, t => !!(t.flags & TypeFlags.TypeParameter)); + if (sourceParameters.length !== targetParameters.length) { reportRelationError(headMessage, source, target); return Ternary.False; } - } - const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo; - if (result = objectTypeRelatedTo(source, source, target, reportStructuralErrors)) { - errorInfo = saveErrorInfo; - return result; + for (let i = 0; i < sourceParameters.length; i++) { + if (sourceParameters[i].symbol !== targetParameters[i].symbol) { + reportRelationError(headMessage, source, target); + return Ternary.False; + } + } + const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo; + if (result = objectTypeRelatedTo(source, source, target, reportStructuralErrors)) { + errorInfo = saveErrorInfo; + return result; + } } } From 0f773c5673d85633d321fb4cb5cbed94739d3c78 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 27 Sep 2016 16:17:14 -0700 Subject: [PATCH 015/224] Add object spread scenario tests --- .../reference/objectSpreadScenarios.js | 45 ++++++++ .../reference/objectSpreadScenarios.symbols | 84 ++++++++++++++ .../reference/objectSpreadScenarios.types | 106 ++++++++++++++++++ .../types/spread/objectSpreadScenarios.ts | 17 +++ 4 files changed, 252 insertions(+) create mode 100644 tests/baselines/reference/objectSpreadScenarios.js create mode 100644 tests/baselines/reference/objectSpreadScenarios.symbols create mode 100644 tests/baselines/reference/objectSpreadScenarios.types create mode 100644 tests/cases/conformance/types/spread/objectSpreadScenarios.ts diff --git a/tests/baselines/reference/objectSpreadScenarios.js b/tests/baselines/reference/objectSpreadScenarios.js new file mode 100644 index 0000000000000..ae0e2f6c425ef --- /dev/null +++ b/tests/baselines/reference/objectSpreadScenarios.js @@ -0,0 +1,45 @@ +//// [objectSpreadScenarios.ts] +interface A1 { a: boolean } +interface B1 { b: number }; +function override(initial: U, override: U): U { + return { ...initial, ...override }; +} +function update(this: { u: U }, override: U): void { + this.u = { ...this.u, ...override }; +} +function mixin(one: T, two: U): { ...T, ...U } { + return { ...one, ...two }; +} +let a1: A1 = { a: true }; +let b1: B1 = { b: 101 }; +a1 = override(a1, { a: false }); +let host = { u: a1, update }; +host.update({ a: false }); +let mixed = mixin(a1, b1); + + +//// [objectSpreadScenarios.js] +var __assign = (this && this.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; +}; +; +function override(initial, override) { + return __assign({}, initial, override); +} +function update(override) { + this.u = __assign({}, this.u, override); +} +function mixin(one, two) { + return __assign({}, one, two); +} +var a1 = { a: true }; +var b1 = { b: 101 }; +a1 = override(a1, { a: false }); +var host = { u: a1, update: update }; +host.update({ a: false }); +var mixed = mixin(a1, b1); diff --git a/tests/baselines/reference/objectSpreadScenarios.symbols b/tests/baselines/reference/objectSpreadScenarios.symbols new file mode 100644 index 0000000000000..c8c18ca1e2f5b --- /dev/null +++ b/tests/baselines/reference/objectSpreadScenarios.symbols @@ -0,0 +1,84 @@ +=== tests/cases/conformance/types/spread/objectSpreadScenarios.ts === +interface A1 { a: boolean } +>A1 : Symbol(A1, Decl(objectSpreadScenarios.ts, 0, 0)) +>a : Symbol(A1.a, Decl(objectSpreadScenarios.ts, 0, 14)) + +interface B1 { b: number }; +>B1 : Symbol(B1, Decl(objectSpreadScenarios.ts, 0, 27)) +>b : Symbol(B1.b, Decl(objectSpreadScenarios.ts, 1, 14)) + +function override(initial: U, override: U): U { +>override : Symbol(override, Decl(objectSpreadScenarios.ts, 1, 27)) +>U : Symbol(U, Decl(objectSpreadScenarios.ts, 2, 18)) +>initial : Symbol(initial, Decl(objectSpreadScenarios.ts, 2, 21)) +>U : Symbol(U, Decl(objectSpreadScenarios.ts, 2, 18)) +>override : Symbol(override, Decl(objectSpreadScenarios.ts, 2, 32)) +>U : Symbol(U, Decl(objectSpreadScenarios.ts, 2, 18)) +>U : Symbol(U, Decl(objectSpreadScenarios.ts, 2, 18)) + + return { ...initial, ...override }; +} +function update(this: { u: U }, override: U): void { +>update : Symbol(update, Decl(objectSpreadScenarios.ts, 4, 1)) +>U : Symbol(U, Decl(objectSpreadScenarios.ts, 5, 16)) +>this : Symbol(this, Decl(objectSpreadScenarios.ts, 5, 19)) +>u : Symbol(u, Decl(objectSpreadScenarios.ts, 5, 26)) +>U : Symbol(U, Decl(objectSpreadScenarios.ts, 5, 16)) +>override : Symbol(override, Decl(objectSpreadScenarios.ts, 5, 34)) +>U : Symbol(U, Decl(objectSpreadScenarios.ts, 5, 16)) + + this.u = { ...this.u, ...override }; +>this.u : Symbol(u, Decl(objectSpreadScenarios.ts, 5, 26)) +>this : Symbol(this, Decl(objectSpreadScenarios.ts, 5, 19)) +>u : Symbol(u, Decl(objectSpreadScenarios.ts, 5, 26)) +>this.u : Symbol(u, Decl(objectSpreadScenarios.ts, 5, 26)) +>this : Symbol(this, Decl(objectSpreadScenarios.ts, 5, 19)) +>u : Symbol(u, Decl(objectSpreadScenarios.ts, 5, 26)) +} +function mixin(one: T, two: U): { ...T, ...U } { +>mixin : Symbol(mixin, Decl(objectSpreadScenarios.ts, 7, 1)) +>T : Symbol(T, Decl(objectSpreadScenarios.ts, 8, 15)) +>U : Symbol(U, Decl(objectSpreadScenarios.ts, 8, 17)) +>one : Symbol(one, Decl(objectSpreadScenarios.ts, 8, 21)) +>T : Symbol(T, Decl(objectSpreadScenarios.ts, 8, 15)) +>two : Symbol(two, Decl(objectSpreadScenarios.ts, 8, 28)) +>U : Symbol(U, Decl(objectSpreadScenarios.ts, 8, 17)) +>T : Symbol(T, Decl(objectSpreadScenarios.ts, 8, 15)) +>U : Symbol(U, Decl(objectSpreadScenarios.ts, 8, 17)) + + return { ...one, ...two }; +} +let a1: A1 = { a: true }; +>a1 : Symbol(a1, Decl(objectSpreadScenarios.ts, 11, 3)) +>A1 : Symbol(A1, Decl(objectSpreadScenarios.ts, 0, 0)) +>a : Symbol(a, Decl(objectSpreadScenarios.ts, 11, 14)) + +let b1: B1 = { b: 101 }; +>b1 : Symbol(b1, Decl(objectSpreadScenarios.ts, 12, 3)) +>B1 : Symbol(B1, Decl(objectSpreadScenarios.ts, 0, 27)) +>b : Symbol(b, Decl(objectSpreadScenarios.ts, 12, 14)) + +a1 = override(a1, { a: false }); +>a1 : Symbol(a1, Decl(objectSpreadScenarios.ts, 11, 3)) +>override : Symbol(override, Decl(objectSpreadScenarios.ts, 1, 27)) +>a1 : Symbol(a1, Decl(objectSpreadScenarios.ts, 11, 3)) +>a : Symbol(a, Decl(objectSpreadScenarios.ts, 13, 19)) + +let host = { u: a1, update }; +>host : Symbol(host, Decl(objectSpreadScenarios.ts, 14, 3)) +>u : Symbol(u, Decl(objectSpreadScenarios.ts, 14, 12)) +>a1 : Symbol(a1, Decl(objectSpreadScenarios.ts, 11, 3)) +>update : Symbol(update, Decl(objectSpreadScenarios.ts, 14, 19)) + +host.update({ a: false }); +>host.update : Symbol(update, Decl(objectSpreadScenarios.ts, 14, 19)) +>host : Symbol(host, Decl(objectSpreadScenarios.ts, 14, 3)) +>update : Symbol(update, Decl(objectSpreadScenarios.ts, 14, 19)) +>a : Symbol(a, Decl(objectSpreadScenarios.ts, 15, 13)) + +let mixed = mixin(a1, b1); +>mixed : Symbol(mixed, Decl(objectSpreadScenarios.ts, 16, 3)) +>mixin : Symbol(mixin, Decl(objectSpreadScenarios.ts, 7, 1)) +>a1 : Symbol(a1, Decl(objectSpreadScenarios.ts, 11, 3)) +>b1 : Symbol(b1, Decl(objectSpreadScenarios.ts, 12, 3)) + diff --git a/tests/baselines/reference/objectSpreadScenarios.types b/tests/baselines/reference/objectSpreadScenarios.types new file mode 100644 index 0000000000000..eebe08a2aafe7 --- /dev/null +++ b/tests/baselines/reference/objectSpreadScenarios.types @@ -0,0 +1,106 @@ +=== tests/cases/conformance/types/spread/objectSpreadScenarios.ts === +interface A1 { a: boolean } +>A1 : A1 +>a : boolean + +interface B1 { b: number }; +>B1 : B1 +>b : number + +function override(initial: U, override: U): U { +>override : (initial: U, override: U) => U +>U : U +>initial : U +>U : U +>override : U +>U : U +>U : U + + return { ...initial, ...override }; +>{ ...initial, ...override } : { ...U; ...U } +>initial : any +>override : any +} +function update(this: { u: U }, override: U): void { +>update : (this: { u: U; }, override: U) => void +>U : U +>this : { u: U; } +>u : U +>U : U +>override : U +>U : U + + this.u = { ...this.u, ...override }; +>this.u = { ...this.u, ...override } : { ...U; ...U } +>this.u : U +>this : { u: U; } +>u : U +>{ ...this.u, ...override } : { ...U; ...U } +>this.u : U +>this : { u: U; } +>u : U +>override : any +} +function mixin(one: T, two: U): { ...T, ...U } { +>mixin : (one: T, two: U) => { ...T; ...U } +>T : T +>U : U +>one : T +>T : T +>two : U +>U : U +>T : T +>U : U + + return { ...one, ...two }; +>{ ...one, ...two } : { ...T; ...U } +>one : any +>two : any +} +let a1: A1 = { a: true }; +>a1 : A1 +>A1 : A1 +>{ a: true } : { a: true; } +>a : boolean +>true : true + +let b1: B1 = { b: 101 }; +>b1 : B1 +>B1 : B1 +>{ b: 101 } : { b: number; } +>b : number +>101 : 101 + +a1 = override(a1, { a: false }); +>a1 = override(a1, { a: false }) : A1 +>a1 : A1 +>override(a1, { a: false }) : A1 +>override : (initial: U, override: U) => U +>a1 : A1 +>{ a: false } : { a: false; } +>a : boolean +>false : false + +let host = { u: a1, update }; +>host : { u: A1; update: (this: { u: U; }, override: U) => void; } +>{ u: a1, update } : { u: A1; update: (this: { u: U; }, override: U) => void; } +>u : A1 +>a1 : A1 +>update : (this: { u: U; }, override: U) => void + +host.update({ a: false }); +>host.update({ a: false }) : void +>host.update : (this: { u: U; }, override: U) => void +>host : { u: A1; update: (this: { u: U; }, override: U) => void; } +>update : (this: { u: U; }, override: U) => void +>{ a: false } : { a: false; } +>a : boolean +>false : false + +let mixed = mixin(a1, b1); +>mixed : { ...A1; ...B1 } +>mixin(a1, b1) : { ...A1; ...B1 } +>mixin : (one: T, two: U) => { ...T; ...U } +>a1 : A1 +>b1 : B1 + diff --git a/tests/cases/conformance/types/spread/objectSpreadScenarios.ts b/tests/cases/conformance/types/spread/objectSpreadScenarios.ts new file mode 100644 index 0000000000000..2f944ddb7966e --- /dev/null +++ b/tests/cases/conformance/types/spread/objectSpreadScenarios.ts @@ -0,0 +1,17 @@ +interface A1 { a: boolean } +interface B1 { b: number }; +function override(initial: U, override: U): U { + return { ...initial, ...override }; +} +function update(this: { u: U }, override: U): void { + this.u = { ...this.u, ...override }; +} +function mixin(one: T, two: U): { ...T, ...U } { + return { ...one, ...two }; +} +let a1: A1 = { a: true }; +let b1: B1 = { b: 101 }; +a1 = override(a1, { a: false }); +let host = { u: a1, update }; +host.update({ a: false }); +let mixed = mixin(a1, b1); From 62c5bda3bbc1ee06d6ed0a575274da3159004f52 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 28 Sep 2016 14:14:53 -0700 Subject: [PATCH 016/224] isDeclaredProperty: Use optional-boolean idiom As elsewhere in the compiler code --- src/compiler/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0e120bd74f062..7a98efd007f8f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2536,7 +2536,7 @@ namespace ts { /* @internal */ export interface SpreadElementType extends ResolvedType { - isDeclaredProperty: boolean | undefined; + isDeclaredProperty?: boolean; } /* @internal */ From b9af986df1d4e277af6464033be98fbc666642d9 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 3 Oct 2016 15:18:04 -0700 Subject: [PATCH 017/224] Update object spread scenarios test --- tests/baselines/reference/objectSpreadScenarios.js | 2 +- .../baselines/reference/objectSpreadScenarios.symbols | 3 ++- tests/baselines/reference/objectSpreadScenarios.types | 11 ++++++----- .../conformance/types/spread/objectSpreadScenarios.ts | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/baselines/reference/objectSpreadScenarios.js b/tests/baselines/reference/objectSpreadScenarios.js index ae0e2f6c425ef..64739a2b298b0 100644 --- a/tests/baselines/reference/objectSpreadScenarios.js +++ b/tests/baselines/reference/objectSpreadScenarios.js @@ -1,7 +1,7 @@ //// [objectSpreadScenarios.ts] interface A1 { a: boolean } interface B1 { b: number }; -function override(initial: U, override: U): U { +function override(initial: U, override: U): { ...U, ...U } { return { ...initial, ...override }; } function update(this: { u: U }, override: U): void { diff --git a/tests/baselines/reference/objectSpreadScenarios.symbols b/tests/baselines/reference/objectSpreadScenarios.symbols index c8c18ca1e2f5b..784e340a10ce2 100644 --- a/tests/baselines/reference/objectSpreadScenarios.symbols +++ b/tests/baselines/reference/objectSpreadScenarios.symbols @@ -7,13 +7,14 @@ interface B1 { b: number }; >B1 : Symbol(B1, Decl(objectSpreadScenarios.ts, 0, 27)) >b : Symbol(B1.b, Decl(objectSpreadScenarios.ts, 1, 14)) -function override(initial: U, override: U): U { +function override(initial: U, override: U): { ...U, ...U } { >override : Symbol(override, Decl(objectSpreadScenarios.ts, 1, 27)) >U : Symbol(U, Decl(objectSpreadScenarios.ts, 2, 18)) >initial : Symbol(initial, Decl(objectSpreadScenarios.ts, 2, 21)) >U : Symbol(U, Decl(objectSpreadScenarios.ts, 2, 18)) >override : Symbol(override, Decl(objectSpreadScenarios.ts, 2, 32)) >U : Symbol(U, Decl(objectSpreadScenarios.ts, 2, 18)) +>U : Symbol(U, Decl(objectSpreadScenarios.ts, 2, 18)) >U : Symbol(U, Decl(objectSpreadScenarios.ts, 2, 18)) return { ...initial, ...override }; diff --git a/tests/baselines/reference/objectSpreadScenarios.types b/tests/baselines/reference/objectSpreadScenarios.types index eebe08a2aafe7..5330029afe127 100644 --- a/tests/baselines/reference/objectSpreadScenarios.types +++ b/tests/baselines/reference/objectSpreadScenarios.types @@ -7,13 +7,14 @@ interface B1 { b: number }; >B1 : B1 >b : number -function override(initial: U, override: U): U { ->override : (initial: U, override: U) => U +function override(initial: U, override: U): { ...U, ...U } { +>override : (initial: U, override: U) => { ...U; ...U } >U : U >initial : U >U : U >override : U >U : U +>U : U >U : U return { ...initial, ...override }; @@ -72,10 +73,10 @@ let b1: B1 = { b: 101 }; >101 : 101 a1 = override(a1, { a: false }); ->a1 = override(a1, { a: false }) : A1 +>a1 = override(a1, { a: false }) : { ...A1; ...A1 } >a1 : A1 ->override(a1, { a: false }) : A1 ->override : (initial: U, override: U) => U +>override(a1, { a: false }) : { ...A1; ...A1 } +>override : (initial: U, override: U) => { ...U; ...U } >a1 : A1 >{ a: false } : { a: false; } >a : boolean diff --git a/tests/cases/conformance/types/spread/objectSpreadScenarios.ts b/tests/cases/conformance/types/spread/objectSpreadScenarios.ts index 2f944ddb7966e..c80cffa44e701 100644 --- a/tests/cases/conformance/types/spread/objectSpreadScenarios.ts +++ b/tests/cases/conformance/types/spread/objectSpreadScenarios.ts @@ -1,6 +1,6 @@ interface A1 { a: boolean } interface B1 { b: number }; -function override(initial: U, override: U): U { +function override(initial: U, override: U): { ...U, ...U } { return { ...initial, ...override }; } function update(this: { u: U }, override: U): void { From 57850ecf1ae5cad2a81112b4152133a99c8b9837 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 3 Oct 2016 15:49:23 -0700 Subject: [PATCH 018/224] Add more generic assignability cases --- .../cases/conformance/types/spread/objectSpreadGeneric.ts | 7 +++++++ .../cases/conformance/types/spread/objectSpreadNegative.ts | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/tests/cases/conformance/types/spread/objectSpreadGeneric.ts b/tests/cases/conformance/types/spread/objectSpreadGeneric.ts index 552142004b70f..a6919ce814931 100644 --- a/tests/cases/conformance/types/spread/objectSpreadGeneric.ts +++ b/tests/cases/conformance/types/spread/objectSpreadGeneric.ts @@ -1,5 +1,12 @@ function f(t: T, u: U, v: V): void { let o: { ...T, ...U, ...V }; + let uu: { ...U, ...U}; + let u: { ...U }; + let u0: U; + uu = u; // ok, multiple spreads are equivalent to a single one + u = uu; // ok, multiple spreads are equivalent to a single one + u0 = u; // error, might be missing a ton of stuff + u = u0; // ok, type has at least all the properties of the spread const same: { ...T, ...U, ...V } = o; // ok const reversed: { ...V, ...U, ...T } = o; // error, reversed const reversed2: { ...U, ...T, ...V } = o; // error, U and T are still reversed diff --git a/tests/cases/conformance/types/spread/objectSpreadNegative.ts b/tests/cases/conformance/types/spread/objectSpreadNegative.ts index 4691e45a17df3..4d1cfbe398c32 100644 --- a/tests/cases/conformance/types/spread/objectSpreadNegative.ts +++ b/tests/cases/conformance/types/spread/objectSpreadNegative.ts @@ -47,3 +47,8 @@ callableConstructableSpread(12); // error, no call signature new callableConstructableSpread(12); // error, no construct signature let callableSpread = { ...publicx, ...(n => n + 1) }; // error, can't spread functions + +// { ...U } is not assignable to U +function override(initial: U, override: U): U { + return { ...initial, ...override }; +} From 8421f735322e88f67b4b17feb2720e54c61639ea Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 5 Oct 2016 17:20:42 -0700 Subject: [PATCH 019/224] Report missing global diagnostics --- src/compiler/checker.ts | 172 +++++++++++++++++++++++++++++++++------- src/compiler/core.ts | 33 +++++++- 2 files changed, 173 insertions(+), 32 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ef428f26de913..9840df7d805f8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5787,6 +5787,90 @@ namespace ts { } } + function getStaticTypeFromTypeNode(node: TypeNode) { + switch (node.kind) { + case SyntaxKind.AnyKeyword: + case SyntaxKind.JSDocAllType: + case SyntaxKind.JSDocUnknownType: + return anyType; + case SyntaxKind.StringKeyword: + return stringType; + case SyntaxKind.NumberKeyword: + return numberType; + case SyntaxKind.BooleanKeyword: + return booleanType; + case SyntaxKind.SymbolKeyword: + return esSymbolType; + case SyntaxKind.VoidKeyword: + return voidType; + case SyntaxKind.UndefinedKeyword: + return undefinedType; + case SyntaxKind.NullKeyword: + return nullType; + case SyntaxKind.NeverKeyword: + return neverType; + case SyntaxKind.JSDocNullKeyword: + return nullType; + case SyntaxKind.JSDocUndefinedKeyword: + return undefinedType; + case SyntaxKind.JSDocNeverKeyword: + return neverType; + case SyntaxKind.ThisType: + case SyntaxKind.ThisKeyword: + return getTypeFromThisTypeNode(node); + case SyntaxKind.LiteralType: + return getTypeFromLiteralTypeNode(node); + case SyntaxKind.JSDocLiteralType: + return getTypeFromLiteralTypeNode((node).literal); + case SyntaxKind.TypeReference: + case SyntaxKind.JSDocTypeReference: + return getTypeFromTypeReference(node); + case SyntaxKind.TypePredicate: + return booleanType; + case SyntaxKind.ExpressionWithTypeArguments: + return getTypeFromTypeReference(node); + case SyntaxKind.TypeQuery: + return getTypeFromTypeQueryNode(node); + case SyntaxKind.ArrayType: + case SyntaxKind.JSDocArrayType: + return getTypeFromArrayTypeNode(node); + case SyntaxKind.TupleType: + return getTypeFromTupleTypeNode(node); + case SyntaxKind.UnionType: + case SyntaxKind.JSDocUnionType: + return getTypeFromUnionTypeNode(node, aliasSymbol, aliasTypeArguments); + case SyntaxKind.IntersectionType: + return getTypeFromIntersectionTypeNode(node, aliasSymbol, aliasTypeArguments); + case SyntaxKind.ParenthesizedType: + case SyntaxKind.JSDocNullableType: + case SyntaxKind.JSDocNonNullableType: + case SyntaxKind.JSDocConstructorType: + case SyntaxKind.JSDocThisType: + case SyntaxKind.JSDocOptionalType: + return getTypeFromTypeNode((node).type); + case SyntaxKind.JSDocRecordType: + return getTypeFromTypeNode((node as JSDocRecordType).literal); + case SyntaxKind.FunctionType: + case SyntaxKind.ConstructorType: + case SyntaxKind.TypeLiteral: + case SyntaxKind.JSDocTypeLiteral: + case SyntaxKind.JSDocFunctionType: + return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node, aliasSymbol, aliasTypeArguments); + // This function assumes that an identifier or qualified name is a type expression + // Callers should first ensure this by calling isTypeNode + case SyntaxKind.Identifier: + case SyntaxKind.QualifiedName: + const symbol = getSymbolAtLocation(node); + return symbol && getDeclaredTypeOfSymbol(symbol); + case SyntaxKind.JSDocTupleType: + return getTypeFromJSDocTupleType(node); + case SyntaxKind.JSDocVariadicType: + return getTypeFromJSDocVariadicType(node); + default: + return unknownType; + } + } + function instantiateList(items: T[], mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): T[] { if (items && items.length) { const result: T[] = []; @@ -15181,31 +15265,24 @@ namespace ts { } /** - * Checks the return type of an async function to ensure it is a compatible - * Promise implementation. - * @param node The signature to check - * @param returnType The return type for the function - * @remarks - * This checks that an async function has a valid Promise-compatible return type, - * and returns the *awaited type* of the promise. An async function has a valid - * Promise-compatible return type if the resolved value of the return type has a - * construct signature that takes in an `initializer` function that in turn supplies - * a `resolve` function as one of its arguments and results in an object with a - * callable `then` signature. - */ + * Checks the return type of an async function to ensure it is a compatible + * Promise implementation. + * + * This checks that an async function has a valid Promise-compatible return type, + * and returns the *awaited type* of the promise. An async function has a valid + * Promise-compatible return type if the resolved value of the return type has a + * construct signature that takes in an `initializer` function that in turn supplies + * a `resolve` function as one of its arguments and results in an object with a + * callable `then` signature. + * + * @param node The signature to check + */ function checkAsyncFunctionReturnType(node: FunctionLikeDeclaration): Type { if (languageVersion >= ScriptTarget.ES6) { const returnType = getTypeFromTypeNode(node.type); return checkCorrectPromiseType(returnType, node.type, Diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type); } - const globalPromiseConstructorLikeType = getGlobalPromiseConstructorLikeType(); - if (globalPromiseConstructorLikeType === emptyObjectType) { - // If we couldn't resolve the global PromiseConstructorLike type we cannot verify - // compatibility with __awaiter. - return unknownType; - } - // As part of our emit for an async function, we will need to emit the entity name of // the return type annotation as an expression. To meet the necessary runtime semantics // for __awaiter, we must also check that the type of the declaration (e.g. the static @@ -15230,18 +15307,35 @@ namespace ts { // then(...): Promise; // } // - // When we get the type of the `Promise` symbol here, we get the type of the static - // side of the `Promise` class, which would be `{ new (...): Promise }`. + + const promiseName = getEntityNameFromTypeNode(node.type); + const rootName = getFirstIdentifier(promiseName); + + // Mark the root symbol as referenced. + getSymbolLinks(rootName.symbol).referenced = true; const promiseType = getTypeFromTypeNode(node.type); if (promiseType === unknownType && compilerOptions.isolatedModules) { // If we are compiling with isolatedModules, we may not be able to resolve the - // type as a value. As such, we will just return unknownType; + // type as a value. As such, we will just return unknownType. return unknownType; } + const promiseConstructorType = getStaticTypeFromTypeNode(node.type); + + const globalPromiseConstructorLikeType = getGlobalPromiseConstructorLikeType(); + if (globalPromiseConstructorLikeType === emptyObjectType) { + // If we couldn't resolve the global PromiseConstructorLike type we cannot verify + // compatibility with __awaiter. + error(node.type || node.name || node, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type); + return unknownType; + } + + // When we get the type of the `Promise` symbol here, we get the type of the static + // side of the `Promise` class, which would be `{ new (...): Promise }`. + const promiseConstructor = getNodeLinks(node.type).resolvedSymbol; - if (!promiseConstructor || !symbolIsValue(promiseConstructor)) { + if (!promiseConstructor) { // try to fall back to global promise type. const typeName = promiseConstructor ? symbolToString(promiseConstructor) @@ -15259,12 +15353,10 @@ namespace ts { } // Verify there is no local declaration that could collide with the promise constructor. - const promiseName = getEntityNameFromTypeNode(node.type); - const promiseNameOrNamespaceRoot = getFirstIdentifier(promiseName); - const rootSymbol = getSymbol(node.locals, promiseNameOrNamespaceRoot.text, SymbolFlags.Value); + const rootSymbol = getSymbol(node.locals, rootName.text, SymbolFlags.Value); if (rootSymbol) { error(rootSymbol.valueDeclaration, Diagnostics.Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions, - promiseNameOrNamespaceRoot.text, + rootName.text, getFullyQualifiedName(promiseConstructor)); return unknownType; } @@ -18086,9 +18178,33 @@ namespace ts { function getDiagnosticsWorker(sourceFile: SourceFile): Diagnostic[] { throwIfNonDiagnosticsProducing(); if (sourceFile) { + // Some global diagnostics are deferred until they are needed and + // may not be reported in the firt call to getGlobalDiagnostics. + // We should catch these changes and report them. + const previousGlobalDiagnostics = diagnostics.getGlobalDiagnostics(); + const previousGlobalDiagnosticsSize = previousGlobalDiagnostics.length; + checkSourceFile(sourceFile); - return diagnostics.getDiagnostics(sourceFile.fileName); + + const semanticDiagnostics = diagnostics.getDiagnostics(sourceFile.fileName); + const currentGlobalDiagnostics = diagnostics.getGlobalDiagnostics(); + if (currentGlobalDiagnostics !== previousGlobalDiagnostics) { + // If the arrays are not the same reference, new diagnostics were added. + const deferredGlobalDiagnostics = relativeComplement(previousGlobalDiagnostics, currentGlobalDiagnostics, compareDiagnostics); + return concatenate(deferredGlobalDiagnostics, semanticDiagnostics); + } + else if (previousGlobalDiagnosticsSize === 0 && currentGlobalDiagnostics.length > 0) { + // If the arrays are the same reference, but the length has changed, a single + // new diagnostic was added as DiagnosticCollection attempts to reuse the + // same array. + return concatenate(currentGlobalDiagnostics, semanticDiagnostics); + } + + return semanticDiagnostics; } + + // Global diagnostics are always added when a file is not provided to + // getDiagnostics forEach(host.getSourceFiles(), checkSourceFile); return diagnostics.getDiagnostics(); } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index e63bcbf8474b4..439e56095664f 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -400,8 +400,8 @@ namespace ts { } export function concatenate(array1: T[], array2: T[]): T[] { - if (!array2 || !array2.length) return array1; - if (!array1 || !array1.length) return array2; + if (isEmptyArray(array2)) return array1; + if (isEmptyArray(array1)) return array2; return [...array1, ...array2]; } @@ -443,6 +443,27 @@ namespace ts { return result || array; } + /** + * Gets the relative complement of `arrayA` with respect to `b`, returning the elements that + * are not present in `arrayA` but are present in `arrayB`. Assumes both arrays are sorted + * based on the provided comparer. + */ + export function relativeComplement(arrayA: T[] | undefined, arrayB: T[] | undefined, comparer: (x: T, y: T) => Comparison = compareValues, offsetA: number = 0, offsetB: number = 0): T[] | undefined { + if (!arrayB || !arrayA || arrayB.length === 0 || arrayA.length === 0) return arrayB; + const result: T[] = []; + outer: for (; offsetB < arrayB.length; offsetB++) { + inner: for (; offsetA < arrayA.length; offsetA++) { + switch (comparer(arrayB[offsetB], arrayA[offsetA])) { + case Comparison.LessThan: break inner; + case Comparison.EqualTo: continue outer; + case Comparison.GreaterThan: continue inner; + } + } + result.push(arrayB[offsetB]); + } + return result; + } + export function sum(array: any[], prop: string): number { let result = 0; for (const v of array) { @@ -505,12 +526,12 @@ namespace ts { * @param array A sorted array whose first element must be no larger than number * @param number The value to be searched for in the array. */ - export function binarySearch(array: T[], value: T, comparer?: (v1: T, v2: T) => number): number { + export function binarySearch(array: T[], value: T, comparer?: (v1: T, v2: T) => number, offset?: number): number { if (!array || array.length === 0) { return -1; } - let low = 0; + let low = offset || 0; let high = array.length - 1; comparer = comparer !== undefined ? comparer @@ -829,6 +850,10 @@ namespace ts { return Array.isArray ? Array.isArray(value) : value instanceof Array; } + export function isEmptyArray(value: any[] | undefined): boolean { + return !value || !value.length; + } + export function memoize(callback: () => T): () => T { let value: T; return () => { From 763e78dc066bc26e2e4caa08d600b702e1f2fd4f Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 6 Oct 2016 10:33:44 -0700 Subject: [PATCH 020/224] Simplify async return type checking --- src/compiler/checker.ts | 209 +++++------------- src/compiler/diagnosticMessages.json | 2 +- src/compiler/transformers/ts.ts | 15 +- src/compiler/utilities.ts | 35 ++- .../asyncAliasReturnType_es5.errors.txt | 10 + .../asyncAwaitIsolatedModules_es5.js | 1 + .../asyncFunctionDeclaration15_es5.errors.txt | 24 +- 7 files changed, 108 insertions(+), 188 deletions(-) create mode 100644 tests/baselines/reference/asyncAliasReturnType_es5.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9840df7d805f8..da5bd3a659038 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5787,90 +5787,6 @@ namespace ts { } } - function getStaticTypeFromTypeNode(node: TypeNode) { - switch (node.kind) { - case SyntaxKind.AnyKeyword: - case SyntaxKind.JSDocAllType: - case SyntaxKind.JSDocUnknownType: - return anyType; - case SyntaxKind.StringKeyword: - return stringType; - case SyntaxKind.NumberKeyword: - return numberType; - case SyntaxKind.BooleanKeyword: - return booleanType; - case SyntaxKind.SymbolKeyword: - return esSymbolType; - case SyntaxKind.VoidKeyword: - return voidType; - case SyntaxKind.UndefinedKeyword: - return undefinedType; - case SyntaxKind.NullKeyword: - return nullType; - case SyntaxKind.NeverKeyword: - return neverType; - case SyntaxKind.JSDocNullKeyword: - return nullType; - case SyntaxKind.JSDocUndefinedKeyword: - return undefinedType; - case SyntaxKind.JSDocNeverKeyword: - return neverType; - case SyntaxKind.ThisType: - case SyntaxKind.ThisKeyword: - return getTypeFromThisTypeNode(node); - case SyntaxKind.LiteralType: - return getTypeFromLiteralTypeNode(node); - case SyntaxKind.JSDocLiteralType: - return getTypeFromLiteralTypeNode((node).literal); - case SyntaxKind.TypeReference: - case SyntaxKind.JSDocTypeReference: - return getTypeFromTypeReference(node); - case SyntaxKind.TypePredicate: - return booleanType; - case SyntaxKind.ExpressionWithTypeArguments: - return getTypeFromTypeReference(node); - case SyntaxKind.TypeQuery: - return getTypeFromTypeQueryNode(node); - case SyntaxKind.ArrayType: - case SyntaxKind.JSDocArrayType: - return getTypeFromArrayTypeNode(node); - case SyntaxKind.TupleType: - return getTypeFromTupleTypeNode(node); - case SyntaxKind.UnionType: - case SyntaxKind.JSDocUnionType: - return getTypeFromUnionTypeNode(node, aliasSymbol, aliasTypeArguments); - case SyntaxKind.IntersectionType: - return getTypeFromIntersectionTypeNode(node, aliasSymbol, aliasTypeArguments); - case SyntaxKind.ParenthesizedType: - case SyntaxKind.JSDocNullableType: - case SyntaxKind.JSDocNonNullableType: - case SyntaxKind.JSDocConstructorType: - case SyntaxKind.JSDocThisType: - case SyntaxKind.JSDocOptionalType: - return getTypeFromTypeNode((node).type); - case SyntaxKind.JSDocRecordType: - return getTypeFromTypeNode((node as JSDocRecordType).literal); - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - case SyntaxKind.TypeLiteral: - case SyntaxKind.JSDocTypeLiteral: - case SyntaxKind.JSDocFunctionType: - return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node, aliasSymbol, aliasTypeArguments); - // This function assumes that an identifier or qualified name is a type expression - // Callers should first ensure this by calling isTypeNode - case SyntaxKind.Identifier: - case SyntaxKind.QualifiedName: - const symbol = getSymbolAtLocation(node); - return symbol && getDeclaredTypeOfSymbol(symbol); - case SyntaxKind.JSDocTupleType: - return getTypeFromJSDocTupleType(node); - case SyntaxKind.JSDocVariadicType: - return getTypeFromJSDocVariadicType(node); - default: - return unknownType; - } - } - function instantiateList(items: T[], mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): T[] { if (items && items.length) { const result: T[] = []; @@ -15308,56 +15224,55 @@ namespace ts { // } // - const promiseName = getEntityNameFromTypeNode(node.type); - const rootName = getFirstIdentifier(promiseName); - - // Mark the root symbol as referenced. - getSymbolLinks(rootName.symbol).referenced = true; + // Always mark the type node as referenced if it points to a value + markTypeNodeAsReferenced(node.type); + const promiseConstructorName = getEntityNameFromTypeNode(node.type); const promiseType = getTypeFromTypeNode(node.type); - if (promiseType === unknownType && compilerOptions.isolatedModules) { - // If we are compiling with isolatedModules, we may not be able to resolve the - // type as a value. As such, we will just return unknownType. + if (promiseType === unknownType) { + if (!compilerOptions.isolatedModules) { + if (promiseConstructorName) { + error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName)); + } + else { + error(node.type, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type); + } + } + return unknownType; + } + + if (promiseConstructorName === undefined) { + error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, typeToString(promiseType)); return unknownType; } - const promiseConstructorType = getStaticTypeFromTypeNode(node.type); + const promiseConstructorSymbol = resolveEntityName(promiseConstructorName, SymbolFlags.Value, /*ignoreErrors*/ true); + const promiseConstructorType = promiseConstructorSymbol ? getTypeOfSymbol(promiseConstructorSymbol) : unknownType; + if (promiseConstructorType === unknownType) { + error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName)); + return unknownType; + } const globalPromiseConstructorLikeType = getGlobalPromiseConstructorLikeType(); if (globalPromiseConstructorLikeType === emptyObjectType) { // If we couldn't resolve the global PromiseConstructorLike type we cannot verify // compatibility with __awaiter. - error(node.type || node.name || node, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type); + error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName)); return unknownType; } - // When we get the type of the `Promise` symbol here, we get the type of the static - // side of the `Promise` class, which would be `{ new (...): Promise }`. - - const promiseConstructor = getNodeLinks(node.type).resolvedSymbol; - if (!promiseConstructor) { - // try to fall back to global promise type. - const typeName = promiseConstructor - ? symbolToString(promiseConstructor) - : typeToString(promiseType); - return checkCorrectPromiseType(promiseType, node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type, typeName); - } - - // If the Promise constructor, resolved locally, is an alias symbol we should mark it as referenced. - checkReturnTypeAnnotationAsExpression(node); - - // Validate the promise constructor type. - const promiseConstructorType = getTypeOfSymbol(promiseConstructor); - if (!checkTypeAssignableTo(promiseConstructorType, globalPromiseConstructorLikeType, node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type)) { + if (!checkTypeAssignableTo(promiseConstructorType, globalPromiseConstructorLikeType, node.type, + Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value)) { return unknownType; } // Verify there is no local declaration that could collide with the promise constructor. - const rootSymbol = getSymbol(node.locals, rootName.text, SymbolFlags.Value); - if (rootSymbol) { - error(rootSymbol.valueDeclaration, Diagnostics.Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions, + const rootName = promiseConstructorName && getFirstIdentifier(promiseConstructorName); + const collidingSymbol = getSymbol(node.locals, rootName.text, SymbolFlags.Value); + if (collidingSymbol) { + error(collidingSymbol.valueDeclaration, Diagnostics.Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions, rootName.text, - getFullyQualifiedName(promiseConstructor)); + entityNameToString(promiseConstructorName)); return unknownType; } @@ -15415,44 +15330,19 @@ namespace ts { errorInfo); } - /** Checks a type reference node as an expression. */ - function checkTypeNodeAsExpression(node: TypeNode) { - // When we are emitting type metadata for decorators, we need to try to check the type - // as if it were an expression so that we can emit the type in a value position when we - // serialize the type metadata. - if (node && node.kind === SyntaxKind.TypeReference) { - const root = getFirstIdentifier((node).typeName); - const meaning = root.parent.kind === SyntaxKind.TypeReference ? SymbolFlags.Type : SymbolFlags.Namespace; - // Resolve type so we know which symbol is referenced - const rootSymbol = resolveName(root, root.text, meaning | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined); - // Resolved symbol is alias - if (rootSymbol && rootSymbol.flags & SymbolFlags.Alias) { - const aliasTarget = resolveAlias(rootSymbol); - // If alias has value symbol - mark alias as referenced - if (aliasTarget.flags & SymbolFlags.Value && !isConstEnumOrConstEnumOnlyModule(resolveAlias(rootSymbol))) { - markAliasSymbolAsReferenced(rootSymbol); - } - } - } - } - /** - * Checks the type annotation of an accessor declaration or property declaration as - * an expression if it is a type reference to a type with a value declaration. - */ - function checkTypeAnnotationAsExpression(node: VariableLikeDeclaration) { - checkTypeNodeAsExpression((node).type); - } - - function checkReturnTypeAnnotationAsExpression(node: FunctionLikeDeclaration) { - checkTypeNodeAsExpression(node.type); - } - - /** Checks the type annotation of the parameters of a function/method or the constructor of a class as expressions */ - function checkParameterTypeAnnotationsAsExpressions(node: FunctionLikeDeclaration) { - // ensure all type annotations with a value declaration are checked as an expression - for (const parameter of node.parameters) { - checkTypeAnnotationAsExpression(parameter); + * If a TypeNode can be resolved to a value symbol imported from an external module, it is + * marked as referenced to prevent import elision. + */ + function markTypeNodeAsReferenced(node: TypeNode) { + const typeName = node && getEntityNameFromTypeNode(node); + const rootName = typeName && getFirstIdentifier(typeName); + const rootSymbol = rootName && resolveName(rootName, rootName.text, (typeName.kind === SyntaxKind.Identifier ? SymbolFlags.Type : SymbolFlags.Namespace) | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined); + if (rootSymbol + && rootSymbol.flags & SymbolFlags.Alias + && symbolIsValue(rootSymbol) + && !isConstEnumOrConstEnumOnlyModule(resolveAlias(rootSymbol))) { + markAliasSymbolAsReferenced(rootSymbol); } } @@ -15478,20 +15368,25 @@ namespace ts { case SyntaxKind.ClassDeclaration: const constructor = getFirstConstructorWithBody(node); if (constructor) { - checkParameterTypeAnnotationsAsExpressions(constructor); + for (const parameter of constructor.parameters) { + markTypeNodeAsReferenced(parameter.type); + } } break; case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: - checkParameterTypeAnnotationsAsExpressions(node); - checkReturnTypeAnnotationAsExpression(node); + for (const parameter of (node).parameters) { + markTypeNodeAsReferenced(parameter.type); + } + + markTypeNodeAsReferenced((node).type); break; case SyntaxKind.PropertyDeclaration: case SyntaxKind.Parameter: - checkTypeAnnotationAsExpression(node); + markTypeNodeAsReferenced((node).type); break; } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 71d7a978d1ba0..815ca9a9d6773 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -163,7 +163,7 @@ "category": "Error", "code": 1054 }, - "Type '{0}' is not a valid async function return type.": { + "Type '{0}' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.": { "category": "Error", "code": 1055 }, diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 2419ffa4c3b73..352d6f6f1dc8b 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -2329,15 +2329,16 @@ namespace ts { } function getPromiseConstructor(type: TypeNode) { - const typeName = getEntityNameFromTypeNode(type); - if (typeName && isEntityName(typeName)) { - const serializationKind = resolver.getTypeReferenceSerializationKind(typeName); - if (serializationKind === TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue - || serializationKind === TypeReferenceSerializationKind.Unknown) { - return typeName; + if (type) { + const typeName = getEntityNameFromTypeNode(type); + if (typeName && isEntityName(typeName)) { + const serializationKind = resolver.getTypeReferenceSerializationKind(typeName); + if (serializationKind === TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue + || serializationKind === TypeReferenceSerializationKind.Unknown) { + return typeName; + } } } - return undefined; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 62813e9447c87..b6d81e3f242cf 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -503,6 +503,17 @@ namespace ts { return getFullWidth(name) === 0 ? "(Missing)" : getTextOfNode(name); } + export function entityNameToString(name: EntityNameOrEntityNameExpression): string { + switch (name.kind) { + case SyntaxKind.Identifier: + return getFullWidth(name) === 0 ? unescapeIdentifier((name).text) : getTextOfNode(name); + case SyntaxKind.QualifiedName: + return entityNameToString((name).left) + "." + entityNameToString((name).right); + case SyntaxKind.PropertyAccessExpression: + return entityNameToString((name).expression) + "." + entityNameToString((name).name); + } + } + export function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): Diagnostic { const sourceFile = getSourceFileOfNode(node); const span = getErrorSpanForNode(sourceFile, node); @@ -1054,17 +1065,19 @@ namespace ts { } export function getEntityNameFromTypeNode(node: TypeNode): EntityNameOrEntityNameExpression { - if (node) { - switch (node.kind) { - case SyntaxKind.TypeReference: - return (node).typeName; - case SyntaxKind.ExpressionWithTypeArguments: - Debug.assert(isEntityNameExpression((node).expression)); - return (node).expression; - case SyntaxKind.Identifier: - case SyntaxKind.QualifiedName: - return (node); - } + switch (node.kind) { + case SyntaxKind.TypeReference: + case SyntaxKind.JSDocTypeReference: + return (node).typeName; + + case SyntaxKind.ExpressionWithTypeArguments: + return isEntityNameExpression((node).expression) + ? (node).expression + : undefined; + + case SyntaxKind.Identifier: + case SyntaxKind.QualifiedName: + return (node); } return undefined; diff --git a/tests/baselines/reference/asyncAliasReturnType_es5.errors.txt b/tests/baselines/reference/asyncAliasReturnType_es5.errors.txt new file mode 100644 index 0000000000000..5e524e265e229 --- /dev/null +++ b/tests/baselines/reference/asyncAliasReturnType_es5.errors.txt @@ -0,0 +1,10 @@ +tests/cases/conformance/async/es5/asyncAliasReturnType_es5.ts(3,21): error TS1055: Type 'PromiseAlias' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. + + +==== tests/cases/conformance/async/es5/asyncAliasReturnType_es5.ts (1 errors) ==== + type PromiseAlias = Promise; + + async function f(): PromiseAlias { + ~~~~~~~~~~~~~~~~~~ +!!! error TS1055: Type 'PromiseAlias' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. + } \ No newline at end of file diff --git a/tests/baselines/reference/asyncAwaitIsolatedModules_es5.js b/tests/baselines/reference/asyncAwaitIsolatedModules_es5.js index 7eb2157c02663..d1d1db3e32979 100644 --- a/tests/baselines/reference/asyncAwaitIsolatedModules_es5.js +++ b/tests/baselines/reference/asyncAwaitIsolatedModules_es5.js @@ -77,6 +77,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) { } }; var _this = this; +var missing_1 = require("missing"); function f0() { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/]; diff --git a/tests/baselines/reference/asyncFunctionDeclaration15_es5.errors.txt b/tests/baselines/reference/asyncFunctionDeclaration15_es5.errors.txt index 3ea03927aa9ba..5fa332339124a 100644 --- a/tests/baselines/reference/asyncFunctionDeclaration15_es5.errors.txt +++ b/tests/baselines/reference/asyncFunctionDeclaration15_es5.errors.txt @@ -1,9 +1,9 @@ -tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(6,23): error TS1055: Type '{}' is not a valid async function return type. -tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(7,23): error TS1055: Type 'any' is not a valid async function return type. -tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(8,23): error TS1055: Type 'number' is not a valid async function return type. -tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(9,23): error TS1055: Type 'PromiseLike' is not a valid async function return type. -tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(10,23): error TS1055: Type 'typeof Thenable' is not a valid async function return type. -tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(10,23): error TS1055: Type 'typeof Thenable' is not a valid async function return type. +tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(6,23): error TS1055: Type '{}' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. +tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(7,23): error TS1055: Type 'any' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. +tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(8,23): error TS1055: Type 'number' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. +tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(9,23): error TS1055: Type 'PromiseLike' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. +tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(10,23): error TS1055: Type 'typeof Thenable' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. +tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(10,23): error TS1055: Type 'typeof Thenable' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. Type 'Thenable' is not assignable to type 'PromiseLike'. Types of property 'then' are incompatible. Type '() => void' is not assignable to type '{ (onfulfilled?: (value: any) => any, onrejected?: (reason: any) => any): PromiseLike; (onfulfilled: (value: any) => any, onrejected: (reason: any) => TResult | PromiseLike): PromiseLike; (onfulfilled: (value: any) => TResult | PromiseLike, onrejected?: (reason: any) => TResult | PromiseLike): PromiseLike; (onfulfilled: (value: any) => TResult1 | PromiseLike, onrejected: (reason: any) => TResult2 | PromiseLike): PromiseLike; }'. @@ -20,21 +20,21 @@ tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration1 async function fn1() { } // valid: Promise async function fn2(): { } { } // error ~~~ -!!! error TS1055: Type '{}' is not a valid async function return type. +!!! error TS1055: Type '{}' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. async function fn3(): any { } // error ~~~ -!!! error TS1055: Type 'any' is not a valid async function return type. +!!! error TS1055: Type 'any' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. async function fn4(): number { } // error ~~~~~~ -!!! error TS1055: Type 'number' is not a valid async function return type. +!!! error TS1055: Type 'number' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. async function fn5(): PromiseLike { } // error ~~~~~~~~~~~~~~~~~ -!!! error TS1055: Type 'PromiseLike' is not a valid async function return type. +!!! error TS1055: Type 'PromiseLike' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. async function fn6(): Thenable { } // error ~~~~~~~~ -!!! error TS1055: Type 'typeof Thenable' is not a valid async function return type. +!!! error TS1055: Type 'typeof Thenable' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. ~~~~~~~~ -!!! error TS1055: Type 'typeof Thenable' is not a valid async function return type. +!!! error TS1055: Type 'typeof Thenable' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. !!! error TS1055: Type 'Thenable' is not assignable to type 'PromiseLike'. !!! error TS1055: Types of property 'then' are incompatible. !!! error TS1055: Type '() => void' is not assignable to type '{ (onfulfilled?: (value: any) => any, onrejected?: (reason: any) => any): PromiseLike; (onfulfilled: (value: any) => any, onrejected: (reason: any) => TResult | PromiseLike): PromiseLike; (onfulfilled: (value: any) => TResult | PromiseLike, onrejected?: (reason: any) => TResult | PromiseLike): PromiseLike; (onfulfilled: (value: any) => TResult1 | PromiseLike, onrejected: (reason: any) => TResult2 | PromiseLike): PromiseLike; }'. From aadcbcc083d984f266cfb6188c5dc4dd2cd908fa Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 26 Sep 2016 11:33:25 -0700 Subject: [PATCH 021/224] Use native maps when they're available --- Jakefile.js | 2 + scripts/processDiagnosticMessages.ts | 18 +- src/compiler/binder.ts | 44 +- src/compiler/checker.ts | 480 +++++++------- src/compiler/commandLineParser.ts | 130 ++-- src/compiler/core.ts | 287 +------- src/compiler/dataStructures.ts | 624 ++++++++++++++++++ src/compiler/declarationEmitter.ts | 18 +- src/compiler/emitter.ts | 18 +- src/compiler/factory.ts | 18 +- src/compiler/parser.ts | 6 +- src/compiler/performance.ts | 30 +- src/compiler/program.ts | 72 +- src/compiler/scanner.ts | 21 +- src/compiler/sourcemap.ts | 2 +- src/compiler/sys.ts | 21 +- src/compiler/transformer.ts | 20 +- src/compiler/transformers/es6.ts | 31 +- src/compiler/transformers/generators.ts | 32 +- src/compiler/transformers/jsx.ts | 8 +- src/compiler/transformers/module/module.ts | 48 +- src/compiler/transformers/module/system.ts | 12 +- src/compiler/transformers/ts.ts | 20 +- src/compiler/tsc.ts | 34 +- src/compiler/tsconfig.json | 1 + src/compiler/types.ts | 69 +- src/compiler/utilities.ts | 56 +- src/compiler/visitor.ts | 54 +- src/harness/fourslash.ts | 46 +- src/harness/harness.ts | 27 +- src/harness/harnessLanguageService.ts | 4 +- src/harness/loggedIO.ts | 10 +- src/harness/projectsRunner.ts | 21 +- .../unittests/cachingInServerLSHost.ts | 23 +- src/harness/unittests/moduleResolution.ts | 64 +- .../unittests/reuseProgramStructure.ts | 27 +- src/harness/unittests/session.ts | 22 +- .../unittests/tsserverProjectSystem.ts | 39 +- src/server/builder.ts | 15 +- src/server/client.ts | 8 +- src/server/editorServices.ts | 57 +- src/server/lsHost.ts | 20 +- src/server/project.ts | 51 +- src/server/scriptInfo.ts | 4 +- src/server/session.ts | 10 +- src/server/tsconfig.library.json | 2 +- src/server/typingsCache.ts | 27 +- .../typingsInstaller/typingsInstaller.ts | 42 +- src/server/utilities.ts | 43 +- src/services/classifier.ts | 6 +- src/services/completions.ts | 55 +- src/services/documentHighlights.ts | 6 +- src/services/documentRegistry.ts | 39 +- src/services/findAllReferences.ts | 31 +- src/services/formatting/rules.ts | 2 +- src/services/goToDefinition.ts | 2 +- src/services/jsTyping.ts | 45 +- src/services/navigateTo.ts | 19 +- src/services/navigationBar.ts | 8 +- src/services/patternMatcher.ts | 8 +- src/services/services.ts | 28 +- src/services/shims.ts | 2 +- src/services/signatureHelp.ts | 2 +- src/services/transpile.ts | 6 +- src/services/types.ts | 4 +- tslint.json | 3 +- 66 files changed, 1678 insertions(+), 1326 deletions(-) create mode 100644 src/compiler/dataStructures.ts diff --git a/Jakefile.js b/Jakefile.js index 64a8553ef39dc..2e4a30c90c61a 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -57,6 +57,7 @@ function measure(marker) { } var compilerSources = [ + "dataStructures.ts", "core.ts", "performance.ts", "sys.ts", @@ -91,6 +92,7 @@ var compilerSources = [ }); var servicesSources = [ + "dataStructures.ts", "core.ts", "performance.ts", "sys.ts", diff --git a/scripts/processDiagnosticMessages.ts b/scripts/processDiagnosticMessages.ts index e5eaa46c8e5b9..d6133e6dab0a5 100644 --- a/scripts/processDiagnosticMessages.ts +++ b/scripts/processDiagnosticMessages.ts @@ -27,7 +27,7 @@ function main(): void { var inputFilePath = sys.args[0].replace(/\\/g, "/"); var inputStr = sys.readFile(inputFilePath); - + var diagnosticMessages: InputDiagnosticMessageTable = JSON.parse(inputStr); var names = Utilities.getObjectKeys(diagnosticMessages); @@ -44,7 +44,7 @@ function main(): void { function checkForUniqueCodes(messages: string[], diagnosticTable: InputDiagnosticMessageTable) { const originalMessageForCode: string[] = []; let numConflicts = 0; - + for (const currentMessage of messages) { const code = diagnosticTable[currentMessage].code; @@ -68,19 +68,19 @@ function checkForUniqueCodes(messages: string[], diagnosticTable: InputDiagnosti } } -function buildUniqueNameMap(names: string[]): ts.Map { - var nameMap = ts.createMap(); +function buildUniqueNameMap(names: string[]): ts.Map { + var nameMap = new ts.StringMap(); var uniqueNames = NameGenerator.ensureUniqueness(names, /* isCaseSensitive */ false, /* isFixed */ undefined); for (var i = 0; i < names.length; i++) { - nameMap[names[i]] = uniqueNames[i]; + nameMap.set(names[i], uniqueNames[i]); } return nameMap; } -function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, nameMap: ts.Map): string { +function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, nameMap: ts.Map): string { var result = '// \r\n' + '/// \r\n' + @@ -91,7 +91,7 @@ function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, nameMap: for (var i = 0; i < names.length; i++) { var name = names[i]; var diagnosticDetails = messageTable[name]; - var propName = convertPropertyName(nameMap[name]); + var propName = convertPropertyName(nameMap.get(name)); result += ' ' + propName + @@ -107,14 +107,14 @@ function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, nameMap: return result; } -function buildDiagnosticMessageOutput(messageTable: InputDiagnosticMessageTable, nameMap: ts.Map): string { +function buildDiagnosticMessageOutput(messageTable: InputDiagnosticMessageTable, nameMap: ts.Map): string { var result = '{'; var names = Utilities.getObjectKeys(messageTable); for (var i = 0; i < names.length; i++) { var name = names[i]; var diagnosticDetails = messageTable[name]; - var propName = convertPropertyName(nameMap[name]); + var propName = convertPropertyName(nameMap.get(name)); result += '\r\n "' + createKey(propName, diagnosticDetails.code) + '"' + ' : "' + name.replace(/[\"]/g, '\\"') + '"'; if (i !== names.length - 1) { diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 04b115b4dd9dd..deee2061005c0 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -126,7 +126,7 @@ namespace ts { let symbolCount = 0; let Symbol: { new (flags: SymbolFlags, name: string): Symbol }; - let classifiableNames: Map; + let classifiableNames: Set; const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; const reportedUnreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; @@ -140,7 +140,7 @@ namespace ts { options = opts; languageVersion = getEmitScriptTarget(options); inStrictMode = !!file.externalModuleIndicator; - classifiableNames = createMap(); + classifiableNames = new StringSet(); symbolCount = 0; skipTransformFlagAggregation = isDeclarationFile(file); @@ -190,11 +190,11 @@ namespace ts { symbol.declarations.push(node); if (symbolFlags & SymbolFlags.HasExports && !symbol.exports) { - symbol.exports = createMap(); + symbol.exports = new StringMap(); } if (symbolFlags & SymbolFlags.HasMembers && !symbol.members) { - symbol.members = createMap(); + symbol.members = new StringMap(); } if (symbolFlags & SymbolFlags.Value) { @@ -332,17 +332,17 @@ namespace ts { // Otherwise, we'll be merging into a compatible existing symbol (for example when // you have multiple 'vars' with the same name in the same container). In this case // just add this node into the declarations list of the symbol. - symbol = symbolTable[name] || (symbolTable[name] = createSymbol(SymbolFlags.None, name)); + symbol = getOrUpdate(symbolTable, name, name => createSymbol(SymbolFlags.None, name)); if (name && (includes & SymbolFlags.Classifiable)) { - classifiableNames[name] = name; + classifiableNames.add(name); } if (symbol.flags & excludes) { if (symbol.isReplaceableByMethod) { // Javascript constructor-declared symbols can be discarded in favor of // prototype symbols like methods. - symbol = symbolTable[name] = createSymbol(SymbolFlags.None, name); + symbol = setAndReturn(symbolTable, name, createSymbol(SymbolFlags.None, name)); } else { if (node.name) { @@ -450,7 +450,7 @@ namespace ts { if (containerFlags & ContainerFlags.IsContainer) { container = blockScopeContainer = node; if (containerFlags & ContainerFlags.HasLocals) { - container.locals = createMap(); + container.locals = new StringMap(); } addToContainerChain(container); } @@ -1447,8 +1447,7 @@ namespace ts { const typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, "__type"); addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral); - typeLiteralSymbol.members = createMap(); - typeLiteralSymbol.members[symbol.name] = symbol; + typeLiteralSymbol.members = createMapWithEntry(symbol.name, symbol); } function bindObjectLiteralExpression(node: ObjectLiteralExpression) { @@ -1458,7 +1457,7 @@ namespace ts { } if (inStrictMode) { - const seen = createMap(); + const seen = new StringMap(); for (const prop of node.properties) { if (prop.name.kind !== SyntaxKind.Identifier) { @@ -1479,9 +1478,9 @@ namespace ts { ? ElementKind.Property : ElementKind.Accessor; - const existingKind = seen[identifier.text]; + const existingKind = seen.get(identifier.text); if (!existingKind) { - seen[identifier.text] = currentKind; + seen.set(identifier.text, currentKind); continue; } @@ -1514,7 +1513,7 @@ namespace ts { // fall through. default: if (!blockScopeContainer.locals) { - blockScopeContainer.locals = createMap(); + blockScopeContainer.locals = new StringMap(); addToContainerChain(blockScopeContainer); } declareSymbol(blockScopeContainer.locals, undefined, node, symbolFlags, symbolExcludes); @@ -1976,7 +1975,7 @@ namespace ts { } } - file.symbol.globalExports = file.symbol.globalExports || createMap(); + file.symbol.globalExports = file.symbol.globalExports || new StringMap(); declareSymbol(file.symbol.globalExports, file.symbol, node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); } @@ -2021,7 +2020,7 @@ namespace ts { Debug.assert(isInJavaScriptFile(node)); // Declare a 'member' if the container is an ES5 class or ES6 constructor if (container.kind === SyntaxKind.FunctionDeclaration || container.kind === SyntaxKind.FunctionExpression) { - container.symbol.members = container.symbol.members || createMap(); + container.symbol.members = container.symbol.members || new StringMap(); // It's acceptable for multiple 'this' assignments of the same identifier to occur declareSymbol(container.symbol.members, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property); } @@ -2053,14 +2052,14 @@ namespace ts { constructorFunction.parent = classPrototype; classPrototype.parent = leftSideOfAssignment; - const funcSymbol = container.locals[constructorFunction.text]; + const funcSymbol = container.locals.get(constructorFunction.text); if (!funcSymbol || !(funcSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(funcSymbol))) { return; } // Set up the members collection if it doesn't exist already if (!funcSymbol.members) { - funcSymbol.members = createMap(); + funcSymbol.members = new StringMap(); } // Declare the method/property @@ -2093,7 +2092,7 @@ namespace ts { bindAnonymousDeclaration(node, SymbolFlags.Class, bindingName); // Add name of class expression into the map for semantic classifier if (node.name) { - classifiableNames[node.name.text] = node.name.text; + classifiableNames.add(node.name.text); } } @@ -2109,14 +2108,15 @@ namespace ts { // module might have an exported variable called 'prototype'. We can't allow that as // that would clash with the built-in 'prototype' for the class. const prototypeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Prototype, "prototype"); - if (symbol.exports[prototypeSymbol.name]) { + const symbolExport = symbol.exports.get(prototypeSymbol.name); + if (symbolExport) { if (node.name) { node.name.parent = node; } - file.bindDiagnostics.push(createDiagnosticForNode(symbol.exports[prototypeSymbol.name].declarations[0], + file.bindDiagnostics.push(createDiagnosticForNode(symbolExport.declarations[0], Diagnostics.Duplicate_identifier_0, prototypeSymbol.name)); } - symbol.exports[prototypeSymbol.name] = prototypeSymbol; + symbol.exports.set(prototypeSymbol.name, prototypeSymbol); prototypeSymbol.parent = symbol; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8fef558968e34..941bfdfac8bbd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -47,7 +47,7 @@ namespace ts { let symbolCount = 0; const emptyArray: any[] = []; - const emptySymbols = createMap(); + const emptySymbols = new StringMap(); const compilerOptions = host.getCompilerOptions(); const languageVersion = compilerOptions.target || ScriptTarget.ES3; @@ -111,10 +111,10 @@ namespace ts { }; const tupleTypes: GenericType[] = []; - const unionTypes = createMap(); - const intersectionTypes = createMap(); - const stringLiteralTypes = createMap(); - const numericLiteralTypes = createMap(); + const unionTypes = new StringMap(); + const intersectionTypes = new StringMap(); + const stringLiteralTypes = new StringMap(); + const numericLiteralTypes = new StringMap(); const unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown"); const resolvingSymbol = createSymbol(SymbolFlags.Transient, "__resolving__"); @@ -137,7 +137,7 @@ namespace ts { const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); const emptyGenericType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - emptyGenericType.instantiations = createMap(); + emptyGenericType.instantiations = new StringMap(); const anyFunctionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); // The anyFunctionType contains the anyFunctionType by definition. The flag is further propagated @@ -153,7 +153,7 @@ namespace ts { const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true); - const globals = createMap(); + const globals = new StringMap(); /** * List of every ambient module with a "*" wildcard. * Unlike other ambient modules, these can't be stored in `globals` because symbol tables only deal with exact matches. @@ -218,7 +218,7 @@ namespace ts { const mergedSymbols: Symbol[] = []; const symbolLinks: SymbolLinks[] = []; const nodeLinks: NodeLinks[] = []; - const flowLoopCaches: Map[] = []; + const flowLoopCaches: Map[] = []; const flowLoopNodes: FlowNode[] = []; const flowLoopKeys: string[] = []; const flowLoopTypes: Type[][] = []; @@ -292,7 +292,7 @@ namespace ts { NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy, } - const typeofEQFacts = createMap({ + const typeofEQFacts = mapOfMapLike({ "string": TypeFacts.TypeofEQString, "number": TypeFacts.TypeofEQNumber, "boolean": TypeFacts.TypeofEQBoolean, @@ -302,7 +302,7 @@ namespace ts { "function": TypeFacts.TypeofEQFunction }); - const typeofNEFacts = createMap({ + const typeofNEFacts = mapOfMapLike({ "string": TypeFacts.TypeofNEString, "number": TypeFacts.TypeofNENumber, "boolean": TypeFacts.TypeofNEBoolean, @@ -312,7 +312,7 @@ namespace ts { "function": TypeFacts.TypeofNEFunction }); - const typeofTypesByName = createMap({ + const typeofTypesByName = mapOfMapLike({ "string": stringType, "number": numberType, "boolean": booleanType, @@ -322,7 +322,7 @@ namespace ts { let jsxElementType: ObjectType; /** Things we lazy load from the JSX namespace */ - const jsxTypes = createMap(); + const jsxTypes = new StringMap(); const JsxNames = { JSX: "JSX", IntrinsicElements: "IntrinsicElements", @@ -333,11 +333,11 @@ namespace ts { IntrinsicClassAttributes: "IntrinsicClassAttributes" }; - const subtypeRelation = createMap(); - const assignableRelation = createMap(); - const comparableRelation = createMap(); - const identityRelation = createMap(); - const enumRelation = createMap(); + const subtypeRelation = new StringMap(); + const assignableRelation = new StringMap(); + const comparableRelation = new StringMap(); + const identityRelation = new StringMap(); + const enumRelation = new StringMap(); // This is for caching the result of getSymbolDisplayBuilder. Do not access directly. let _displayBuilder: SymbolDisplayBuilder; @@ -351,8 +351,7 @@ namespace ts { ResolvedReturnType } - const builtinGlobals = createMap(); - builtinGlobals[undefinedSymbol.name] = undefinedSymbol; + const builtinGlobals = createMapWithEntry(undefinedSymbol.name, undefinedSymbol); initializeTypeChecker(); @@ -435,11 +434,11 @@ namespace ts { target.declarations.push(node); }); if (source.members) { - if (!target.members) target.members = createMap(); + if (!target.members) target.members = new StringMap(); mergeSymbolTable(target.members, source.members); } if (source.exports) { - if (!target.exports) target.exports = createMap(); + if (!target.exports) target.exports = new StringMap(); mergeSymbolTable(target.exports, source.exports); } recordMergedSymbol(target, source); @@ -457,18 +456,18 @@ namespace ts { } function mergeSymbolTable(target: SymbolTable, source: SymbolTable) { - for (const id in source) { - let targetSymbol = target[id]; + source.forEach((sourceSymbol, id) => { + let targetSymbol = target.get(id); if (!targetSymbol) { - target[id] = source[id]; + target.set(id, sourceSymbol); } else { if (!(targetSymbol.flags & SymbolFlags.Merged)) { - target[id] = targetSymbol = cloneSymbol(targetSymbol); + target.set(id, targetSymbol = cloneSymbol(targetSymbol)); } - mergeSymbol(targetSymbol, source[id]); + mergeSymbol(targetSymbol, sourceSymbol); } - } + }); } function mergeModuleAugmentation(moduleName: LiteralExpression): void { @@ -509,15 +508,16 @@ namespace ts { } function addToSymbolTable(target: SymbolTable, source: SymbolTable, message: DiagnosticMessage) { - for (const id in source) { - if (target[id]) { + source.forEach((sourceSymbol, id) => { + const symbol = target.get(id); + if (symbol) { // Error on redeclarations - forEach(target[id].declarations, addDeclarationDiagnostic(id, message)); + forEach(symbol.declarations, addDeclarationDiagnostic(id, message)); } else { - target[id] = source[id]; + target.set(id, sourceSymbol); } - } + }); function addDeclarationDiagnostic(id: string, message: DiagnosticMessage) { return (declaration: Declaration) => diagnostics.add(createDiagnosticForNode(declaration, message, id)); @@ -541,7 +541,7 @@ namespace ts { function getSymbol(symbols: SymbolTable, name: string, meaning: SymbolFlags): Symbol { if (meaning) { - const symbol = symbols[name]; + const symbol = symbols.get(name); if (symbol) { Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here."); if (symbol.flags & meaning) { @@ -722,7 +722,7 @@ namespace ts { // It's an external module. First see if the module has an export default and if the local // name of that export default matches. - if (result = moduleExports["default"]) { + if (result = moduleExports.get("default")) { const localSymbol = getLocalSymbolForExportDefault(result); if (localSymbol && (result.flags & meaning) && localSymbol.name === name) { break loop; @@ -741,9 +741,10 @@ namespace ts { // 2. We check === SymbolFlags.Alias in order to check that the symbol is *purely* // an alias. If we used &, we'd be throwing out symbols that have non alias aspects, // which is not the desired behavior. - if (moduleExports[name] && - moduleExports[name].flags === SymbolFlags.Alias && - getDeclarationOfKind(moduleExports[name], SyntaxKind.ExportSpecifier)) { + const moduleExport = moduleExports.get(name); + if (moduleExport && + moduleExport.flags === SymbolFlags.Alias && + getDeclarationOfKind(moduleExport, SyntaxKind.ExportSpecifier)) { break; } } @@ -1058,11 +1059,16 @@ namespace ts { const moduleSymbol = resolveExternalModuleName(node, (node.parent).moduleSpecifier); if (moduleSymbol) { - const exportDefaultSymbol = isShorthandAmbientModuleSymbol(moduleSymbol) ? - moduleSymbol : - moduleSymbol.exports["export="] ? - getPropertyOfType(getTypeOfSymbol(moduleSymbol.exports["export="]), "default") : - resolveSymbol(moduleSymbol.exports["default"]); + let exportDefaultSymbol: Symbol; + if (isShorthandAmbientModuleSymbol(moduleSymbol)) { + exportDefaultSymbol = moduleSymbol; + } + else { + const exportValue = moduleSymbol.exports.get("export="); + exportDefaultSymbol = exportValue + ? getPropertyOfType(getTypeOfSymbol(exportValue), "default") + : resolveSymbol(moduleSymbol.exports.get("default")); + } if (!exportDefaultSymbol && !allowSyntheticDefaultImports) { error(node.name, Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol)); @@ -1112,7 +1118,7 @@ namespace ts { function getExportOfModule(symbol: Symbol, name: string): Symbol { if (symbol.flags & SymbolFlags.Module) { - const exportedSymbol = getExportsOfSymbol(symbol)[name]; + const exportedSymbol = getExportsOfSymbol(symbol).get(name); if (exportedSymbol) { return resolveSymbol(exportedSymbol); } @@ -1140,7 +1146,7 @@ namespace ts { let symbolFromVariable: Symbol; // First check if module was specified with "export=". If so, get the member from the resolved type - if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports["export="]) { + if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports.get("export=")) { symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name.text); } else { @@ -1400,7 +1406,7 @@ namespace ts { // An external module with an 'export =' declaration resolves to the target of the 'export =' declaration, // and an external module with no 'export =' declaration resolves to the module itself. function resolveExternalModuleSymbol(moduleSymbol: Symbol): Symbol { - return moduleSymbol && getMergedSymbol(resolveSymbol(moduleSymbol.exports["export="])) || moduleSymbol; + return moduleSymbol && getMergedSymbol(resolveSymbol(moduleSymbol.exports.get("export="))) || moduleSymbol; } // An external module with an 'export =' declaration may be referenced as an ES6 module provided the 'export =' @@ -1416,7 +1422,7 @@ namespace ts { } function hasExportAssignmentSymbol(moduleSymbol: Symbol): boolean { - return moduleSymbol.exports["export="] !== undefined; + return moduleSymbol.exports.get("export=") !== undefined; } function getExportsOfModuleAsArray(moduleSymbol: Symbol): Symbol[] { @@ -1441,25 +1447,29 @@ namespace ts { * Extends one symbol table with another while collecting information on name collisions for error message generation into the `lookupTable` argument * Not passing `lookupTable` and `exportNode` disables this collection, and just extends the tables */ - function extendExportSymbols(target: SymbolTable, source: SymbolTable, lookupTable?: Map, exportNode?: ExportDeclaration) { - for (const id in source) { - if (id !== "default" && !target[id]) { - target[id] = source[id]; + function extendExportSymbols(target: SymbolTable, source: SymbolTable, lookupTable?: Map, exportNode?: ExportDeclaration) { + if (!source) return; + + source.forEach((sourceSymbol, id) => { + const targetSymbol = target.get(id); + if (id !== "default" && !targetSymbol) { + target.set(id, sourceSymbol); if (lookupTable && exportNode) { - lookupTable[id] = { + lookupTable.set(id, { specifierText: getTextOfNode(exportNode.moduleSpecifier) - } as ExportCollisionTracker; + } as ExportCollisionTracker); } } - else if (lookupTable && exportNode && id !== "default" && target[id] && resolveSymbol(target[id]) !== resolveSymbol(source[id])) { - if (!lookupTable[id].exportsWithDuplicate) { - lookupTable[id].exportsWithDuplicate = [exportNode]; + else if (lookupTable && exportNode && id !== "default" && targetSymbol && resolveSymbol(targetSymbol) !== resolveSymbol(sourceSymbol)) { + const collisionTracker = lookupTable.get(id); + if (!collisionTracker.exportsWithDuplicate) { + collisionTracker.exportsWithDuplicate = [exportNode]; } else { - lookupTable[id].exportsWithDuplicate.push(exportNode); + collisionTracker.exportsWithDuplicate.push(exportNode); } } - } + }); } function getExportsForModule(moduleSymbol: Symbol): SymbolTable { @@ -1475,10 +1485,10 @@ namespace ts { visitedSymbols.push(symbol); const symbols = cloneMap(symbol.exports); // All export * declarations are collected in an __export symbol by the binder - const exportStars = symbol.exports["__export"]; + const exportStars = symbol.exports.get("__export"); if (exportStars) { - const nestedSymbols = createMap(); - const lookupTable = createMap(); + const nestedSymbols = new StringMap(); + const lookupTable = new StringMap(); for (const node of exportStars.declarations) { const resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier); const exportedSymbols = visit(resolvedModule); @@ -1489,21 +1499,20 @@ namespace ts { node as ExportDeclaration ); } - for (const id in lookupTable) { - const { exportsWithDuplicate } = lookupTable[id]; + lookupTable.forEach(({ exportsWithDuplicate }, id) => { // It's not an error if the file with multiple `export *`s with duplicate names exports a member with that name itself - if (id === "export=" || !(exportsWithDuplicate && exportsWithDuplicate.length) || symbols[id]) { - continue; + if (id === "export=" || !(exportsWithDuplicate && exportsWithDuplicate.length) || symbols.get(id)) { + return; } for (const node of exportsWithDuplicate) { diagnostics.add(createDiagnosticForNode( node, Diagnostics.Module_0_has_already_exported_a_member_named_1_Consider_explicitly_re_exporting_to_resolve_the_ambiguity, - lookupTable[id].specifierText, + lookupTable.get(id).specifierText, id )); } - } + }); extendExportSymbols(symbols, nestedSymbols); } return symbols; @@ -1597,15 +1606,14 @@ namespace ts { function getNamedMembers(members: SymbolTable): Symbol[] { let result: Symbol[]; - for (const id in members) { + members.forEach((symbol, id) => { if (!isReservedMemberName(id)) { if (!result) result = []; - const symbol = members[id]; if (symbolIsValue(symbol)) { result.push(symbol); } } - } + }); return result || emptyArray; } @@ -1678,12 +1686,12 @@ namespace ts { } // If symbol is directly available by its name in the symbol table - if (isAccessible(symbols[symbol.name])) { + if (isAccessible(symbols.get(symbol.name))) { return [symbol]; } // Check if symbol is any of the alias - return forEachProperty(symbols, symbolFromSymbolTable => { + return findInMap(symbols, symbolFromSymbolTable => { if (symbolFromSymbolTable.flags & SymbolFlags.Alias && symbolFromSymbolTable.name !== "export=" && !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) { @@ -1718,7 +1726,7 @@ namespace ts { let qualify = false; forEachSymbolTableInScope(enclosingDeclaration, symbolTable => { // If symbol of this name is not available in the symbol table we are ok - let symbolFromSymbolTable = symbolTable[symbol.name]; + let symbolFromSymbolTable = symbolTable.get(symbol.name); if (!symbolFromSymbolTable) { // Continue to the next symbol table return false; @@ -2455,7 +2463,7 @@ namespace ts { } writeIndexSignature(resolved.stringIndexInfo, SyntaxKind.StringKeyword); writeIndexSignature(resolved.numberIndexInfo, SyntaxKind.NumberKeyword); - for (const p of resolved.properties) { + for (const p of sortInV8ObjectInsertionOrder(resolved.properties, p => p.name)) { const t = getTypeOfSymbol(p); if (p.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(t).length) { const signatures = getSignaturesOfType(t, SignatureKind.Call); @@ -3162,7 +3170,7 @@ namespace ts { // Return the type implied by an object binding pattern function getTypeFromObjectBindingPattern(pattern: ObjectBindingPattern, includePatternInType: boolean, reportErrors: boolean): Type { - const members = createMap(); + const members = new StringMap(); let hasComputedProperties = false; forEach(pattern.elements, e => { const name = e.propertyName || e.name; @@ -3177,7 +3185,7 @@ namespace ts { const symbol = createSymbol(flags, text); symbol.type = getTypeFromBindingElement(e, includePatternInType, reportErrors); symbol.bindingElement = e; - members[symbol.name] = symbol; + members.set(symbol.name, symbol); }); const result = createAnonymousType(undefined, members, emptyArray, emptyArray, undefined, undefined); if (includePatternInType) { @@ -3767,8 +3775,7 @@ namespace ts { type.typeParameters = concatenate(outerTypeParameters, localTypeParameters); type.outerTypeParameters = outerTypeParameters; type.localTypeParameters = localTypeParameters; - (type).instantiations = createMap(); - (type).instantiations[getTypeListId(type.typeParameters)] = type; + (type).instantiations = createMapWithEntry(getTypeListId(type.typeParameters), type); (type).target = type; (type).typeArguments = type.typeParameters; type.thisType = createType(TypeFlags.TypeParameter); @@ -3810,8 +3817,7 @@ namespace ts { if (typeParameters) { // Initialize the instantiation cache for generic type aliases. The declared type corresponds to // an instantiation of the type alias with the type parameters supplied as type arguments. - links.instantiations = createMap(); - links.instantiations[getTypeListId(links.typeParameters)] = type; + links.instantiations = createMapWithEntry(getTypeListId(links.typeParameters), type); } } else { @@ -3831,7 +3837,7 @@ namespace ts { return expr.kind === SyntaxKind.NumericLiteral || expr.kind === SyntaxKind.PrefixUnaryExpression && (expr).operator === SyntaxKind.MinusToken && (expr).operand.kind === SyntaxKind.NumericLiteral || - expr.kind === SyntaxKind.Identifier && !!symbol.exports[(expr).text]; + expr.kind === SyntaxKind.Identifier && !!symbol.exports.get((expr).text); } function enumHasLiteralMembers(symbol: Symbol) { @@ -3862,16 +3868,15 @@ namespace ts { enumType.symbol = symbol; if (enumHasLiteralMembers(symbol)) { const memberTypeList: Type[] = []; - const memberTypes = createMap(); + const memberTypes = new NumberMap(); for (const declaration of enumType.symbol.declarations) { if (declaration.kind === SyntaxKind.EnumDeclaration) { computeEnumMemberValues(declaration); for (const member of (declaration).members) { const memberSymbol = getSymbolOfNode(member); const value = getEnumMemberValue(member); - if (!memberTypes[value]) { - const memberType = memberTypes[value] = createEnumLiteralType(memberSymbol, enumType, "" + value); - memberTypeList.push(memberType); + if (!memberTypes.get(value)) { + memberTypeList.push(setAndReturn(memberTypes, value, createEnumLiteralType(memberSymbol, enumType, "" + value))); } } } @@ -3880,7 +3885,7 @@ namespace ts { if (memberTypeList.length > 1) { enumType.flags |= TypeFlags.Union; (enumType).types = memberTypeList; - unionTypes[getTypeListId(memberTypeList)] = enumType; + unionTypes.set(getTypeListId(memberTypeList), enumType); } } } @@ -3892,7 +3897,7 @@ namespace ts { if (!links.declaredType) { const enumType = getDeclaredTypeOfEnum(getParentOfSymbol(symbol)); links.declaredType = enumType.flags & TypeFlags.Union ? - enumType.memberTypes[getEnumMemberValue(symbol.valueDeclaration)] : + enumType.memberTypes.get(getEnumMemberValue(symbol.valueDeclaration)) : enumType; } return links.declaredType; @@ -4022,27 +4027,19 @@ namespace ts { } function createSymbolTable(symbols: Symbol[]): SymbolTable { - const result = createMap(); - for (const symbol of symbols) { - result[symbol.name] = symbol; - } - return result; + return arrayToMap(symbols, symbol => symbol.name); } // The mappingThisOnly flag indicates that the only type parameter being mapped is "this". When the flag is true, // we check symbols to see if we can quickly conclude they are free of "this" references, thus needing no instantiation. function createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper, mappingThisOnly: boolean): SymbolTable { - const result = createMap(); - for (const symbol of symbols) { - result[symbol.name] = mappingThisOnly && isIndependentMember(symbol) ? symbol : instantiateSymbol(symbol, mapper); - } - return result; + return arrayToMap(symbols, symbol => symbol.name, symbol => mappingThisOnly && isIndependentMember(symbol) ? symbol : instantiateSymbol(symbol, mapper)); } function addInheritedMembers(symbols: SymbolTable, baseSymbols: Symbol[]) { for (const s of baseSymbols) { - if (!symbols[s.name]) { - symbols[s.name] = s; + if (!symbols.get(s.name)) { + symbols.set(s.name, s); } } } @@ -4051,8 +4048,8 @@ namespace ts { if (!(type).declaredProperties) { const symbol = type.symbol; (type).declaredProperties = getNamedMembers(symbol.members); - (type).declaredCallSignatures = getSignaturesOfSymbol(symbol.members["__call"]); - (type).declaredConstructSignatures = getSignaturesOfSymbol(symbol.members["__new"]); + (type).declaredCallSignatures = getSignaturesOfSymbol(symbol.members.get("__call")); + (type).declaredConstructSignatures = getSignaturesOfSymbol(symbol.members.get("__new")); (type).declaredStringIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.String); (type).declaredNumberIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.Number); } @@ -4292,8 +4289,8 @@ namespace ts { } else if (symbol.flags & SymbolFlags.TypeLiteral) { const members = symbol.members; - const callSignatures = getSignaturesOfSymbol(members["__call"]); - const constructSignatures = getSignaturesOfSymbol(members["__new"]); + const callSignatures = getSignaturesOfSymbol(members.get("__call")); + const constructSignatures = getSignaturesOfSymbol(members.get("__new")); const stringIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.String); const numberIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.Number); setObjectTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); @@ -4307,7 +4304,7 @@ namespace ts { } if (symbol.flags & SymbolFlags.Class) { const classType = getDeclaredTypeOfClassOrInterface(symbol); - constructSignatures = getSignaturesOfSymbol(symbol.members["__constructor"]); + constructSignatures = getSignaturesOfSymbol(symbol.members.get("__constructor")); if (!constructSignatures.length) { constructSignatures = getDefaultConstructSignatures(classType); } @@ -4363,7 +4360,7 @@ namespace ts { function getPropertyOfObjectType(type: Type, name: string): Symbol { if (type.flags & TypeFlags.ObjectType) { const resolved = resolveStructuredTypeMembers(type); - const symbol = resolved.members[name]; + const symbol = resolved.members.get(name); if (symbol && symbolIsValue(symbol)) { return symbol; } @@ -4384,13 +4381,12 @@ namespace ts { const props = type.resolvedProperties; if (props) { const result: Symbol[] = []; - for (const key in props) { - const prop = props[key]; + props.forEach((prop, key) => { // We need to filter out partial properties in union types if (!(prop.flags & SymbolFlags.SyntheticProperty && (prop).isPartial)) { result.push(prop); } - } + }); return result; } return emptyArray; @@ -4507,12 +4503,12 @@ namespace ts { // these partial properties when identifying discriminant properties, but otherwise they are filtered out // and do not appear to be present in the union type. function getUnionOrIntersectionProperty(type: UnionOrIntersectionType, name: string): Symbol { - const properties = type.resolvedProperties || (type.resolvedProperties = createMap()); - let property = properties[name]; + const properties = type.resolvedProperties || (type.resolvedProperties = new StringMap()); + let property = properties.get(name); if (!property) { property = createUnionOrIntersectionProperty(type, name); if (property) { - properties[name] = property; + properties.set(name, property); } } return property; @@ -4536,7 +4532,7 @@ namespace ts { type = getApparentType(type); if (type.flags & TypeFlags.ObjectType) { const resolved = resolveStructuredTypeMembers(type); - const symbol = resolved.members[name]; + const symbol = resolved.members.get(name); if (symbol && symbolIsValue(symbol)) { return symbol; } @@ -4635,11 +4631,11 @@ namespace ts { function symbolsToArray(symbols: SymbolTable): Symbol[] { const result: Symbol[] = []; - for (const id in symbols) { + symbols.forEach((symbol, id) => { if (!isReservedMemberName(id)) { - result.push(symbols[id]); + result.push(symbol); } - } + }); return result; } @@ -4934,7 +4930,7 @@ namespace ts { } function getIndexSymbol(symbol: Symbol): Symbol { - return symbol.members["__index"]; + return symbol.members.get("__index"); } function getIndexDeclarationOfSymbol(symbol: Symbol, kind: IndexKind): SignatureDeclaration { @@ -5048,11 +5044,11 @@ namespace ts { function createTypeReference(target: GenericType, typeArguments: Type[]): TypeReference { const id = getTypeListId(typeArguments); - let type = target.instantiations[id]; + let type = target.instantiations.get(id); if (!type) { const propagatedFlags = typeArguments ? getPropagatingFlagsOfTypes(typeArguments, /*excludeKinds*/ 0) : 0; const flags = TypeFlags.Reference | propagatedFlags; - type = target.instantiations[id] = createObjectType(flags, target.symbol); + type = setAndReturn(target.instantiations, id, createObjectType(flags, target.symbol)); type.target = target; type.typeArguments = typeArguments; } @@ -5105,7 +5101,7 @@ namespace ts { } const typeArguments = map(node.typeArguments, getTypeFromTypeNodeNoAlias); const id = getTypeListId(typeArguments); - return links.instantiations[id] || (links.instantiations[id] = instantiateType(type, createTypeMapper(typeParameters, typeArguments))); + return links.instantiations.get(id) || setAndReturn(links.instantiations, id, instantiateType(type, createTypeMapper(typeParameters, typeArguments))); } if (node.typeArguments) { error(node, Diagnostics.Type_0_is_not_generic, symbolToString(symbol)); @@ -5333,8 +5329,7 @@ namespace ts { type.typeParameters = typeParameters; type.outerTypeParameters = undefined; type.localTypeParameters = typeParameters; - type.instantiations = createMap(); - type.instantiations[getTypeListId(type.typeParameters)] = type; + type.instantiations = createMapWithEntry(getTypeListId(type.typeParameters), type); type.target = type; type.typeArguments = type.typeParameters; type.thisType = createType(TypeFlags.TypeParameter); @@ -5537,10 +5532,10 @@ namespace ts { return types[0]; } const id = getTypeListId(types); - let type = unionTypes[id]; + let type = unionTypes.get(id); if (!type) { const propagatedFlags = getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable); - type = unionTypes[id] = createObjectType(TypeFlags.Union | propagatedFlags); + type = setAndReturn(unionTypes, id, createObjectType(TypeFlags.Union | propagatedFlags)); type.types = types; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; @@ -5594,10 +5589,10 @@ namespace ts { return typeSet[0]; } const id = getTypeListId(typeSet); - let type = intersectionTypes[id]; + let type = intersectionTypes.get(id); if (!type) { const propagatedFlags = getPropagatingFlagsOfTypes(typeSet, /*excludeKinds*/ TypeFlags.Nullable); - type = intersectionTypes[id] = createObjectType(TypeFlags.Intersection | propagatedFlags); + type = setAndReturn(intersectionTypes, id, createObjectType(TypeFlags.Intersection | propagatedFlags)); type.types = typeSet; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; @@ -5649,7 +5644,7 @@ namespace ts { function getLiteralTypeForText(flags: TypeFlags, text: string) { const map = flags & TypeFlags.StringLiteral ? stringLiteralTypes : numericLiteralTypes; - return map[text] || (map[text] = createLiteralType(flags, text)); + return map.get(text) || setAndReturn(map, text, createLiteralType(flags, text)); } function getTypeFromLiteralTypeNode(node: LiteralTypeNode): Type { @@ -6328,13 +6323,14 @@ namespace ts { return true; } const id = source.id + "," + target.id; - if (enumRelation[id] !== undefined) { - return enumRelation[id]; + const relation = enumRelation.get(id); + if (relation !== undefined) { + return relation; } if (source.symbol.name !== target.symbol.name || !(source.symbol.flags & SymbolFlags.RegularEnum) || !(target.symbol.flags & SymbolFlags.RegularEnum) || (source.flags & TypeFlags.Union) !== (target.flags & TypeFlags.Union)) { - return enumRelation[id] = false; + return setAndReturn(enumRelation, id, false); } const targetEnumType = getTypeOfSymbol(target.symbol); for (const property of getPropertiesOfType(getTypeOfSymbol(source.symbol))) { @@ -6345,14 +6341,14 @@ namespace ts { errorReporter(Diagnostics.Property_0_is_missing_in_type_1, property.name, typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType)); } - return enumRelation[id] = false; + return setAndReturn(enumRelation, id, false); } } } - return enumRelation[id] = true; + return setAndReturn(enumRelation, id, true); } - function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map, errorReporter?: ErrorReporter) { + function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map, errorReporter?: ErrorReporter) { if (target.flags & TypeFlags.Never) return false; if (target.flags & TypeFlags.Any || source.flags & TypeFlags.Never) return true; if (source.flags & TypeFlags.StringLike && target.flags & TypeFlags.String) return true; @@ -6380,7 +6376,7 @@ namespace ts { return false; } - function isTypeRelatedTo(source: Type, target: Type, relation: Map) { + function isTypeRelatedTo(source: Type, target: Type, relation: Map) { if (source.flags & TypeFlags.StringOrNumberLiteral && source.flags & TypeFlags.FreshLiteral) { source = (source).regularType; } @@ -6392,7 +6388,7 @@ namespace ts { } if (source.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType) { const id = relation !== identityRelation || source.id < target.id ? source.id + "," + target.id : target.id + "," + source.id; - const related = relation[id]; + const related = relation.get(id); if (related !== undefined) { return related === RelationComparisonResult.Succeeded; } @@ -6416,7 +6412,7 @@ namespace ts { function checkTypeRelatedTo( source: Type, target: Type, - relation: Map, + relation: Map, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: DiagnosticMessageChain): boolean { @@ -6424,7 +6420,7 @@ namespace ts { let errorInfo: DiagnosticMessageChain; let sourceStack: ObjectType[]; let targetStack: ObjectType[]; - let maybeStack: Map[]; + let maybeStack: Map[]; let expandingFlags: number; let depth = 0; let overflow = false; @@ -6790,12 +6786,12 @@ namespace ts { return Ternary.False; } const id = relation !== identityRelation || source.id < target.id ? source.id + "," + target.id : target.id + "," + source.id; - const related = relation[id]; + const related = relation.get(id); if (related !== undefined) { if (reportErrors && related === RelationComparisonResult.Failed) { // We are elaborating errors and the cached result is an unreported failure. Record the result as a reported // failure and continue computing the relation such that errors get reported. - relation[id] = RelationComparisonResult.FailedAndReported; + relation.set(id, RelationComparisonResult.FailedAndReported); } else { return related === RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False; @@ -6804,7 +6800,7 @@ namespace ts { if (depth > 0) { for (let i = 0; i < depth; i++) { // If source and target are already being compared, consider them related with assumptions - if (maybeStack[i][id]) { + if (maybeStack[i].get(id)) { return Ternary.Maybe; } } @@ -6821,8 +6817,7 @@ namespace ts { } sourceStack[depth] = source; targetStack[depth] = target; - maybeStack[depth] = createMap(); - maybeStack[depth][id] = RelationComparisonResult.Succeeded; + maybeStack[depth] = createMapWithEntry(id, RelationComparisonResult.Succeeded); depth++; const saveExpandingFlags = expandingFlags; if (!(expandingFlags & 1) && isDeeplyNestedGeneric(source, sourceStack, depth)) expandingFlags |= 1; @@ -6852,12 +6847,12 @@ namespace ts { const maybeCache = maybeStack[depth]; // If result is definitely true, copy assumptions to global cache, else copy to next level up const destinationCache = (result === Ternary.True || depth === 0) ? relation : maybeStack[depth - 1]; - copyProperties(maybeCache, destinationCache); + copyMapEntriesFromTo(maybeCache, destinationCache); } else { // A false result goes straight into global cache (when something is false under assumptions it // will also be false without assumptions) - relation[id] = reportErrors ? RelationComparisonResult.FailedAndReported : RelationComparisonResult.Failed; + relation.set(id, reportErrors ? RelationComparisonResult.FailedAndReported : RelationComparisonResult.Failed); } return result; } @@ -7046,14 +7041,29 @@ namespace ts { return result; } - function eachPropertyRelatedTo(source: Type, target: Type, kind: IndexKind, reportErrors: boolean): Ternary { + /** + * For consistency we report the first error in V8 object insertion order. + * Since that's slow and there usually isn't an error, we only sort properties the second time around. + */ + function eachPropertyRelatedTo(source: Type, target: Type, kind: IndexKind, reportErrors: boolean, redoingInV8ObjectInsertionOrder?: boolean): Ternary { let result = Ternary.True; - for (const prop of getPropertiesOfObjectType(source)) { + let properties = getPropertiesOfObjectType(source); + if (redoingInV8ObjectInsertionOrder) { + properties = sortInV8ObjectInsertionOrder(properties, prop => prop.name); + } + for (const prop of properties) { if (kind === IndexKind.String || isNumericLiteralName(prop.name)) { - const related = isRelatedTo(getTypeOfSymbol(prop), target, reportErrors); + const related = isRelatedTo(getTypeOfSymbol(prop), target, reportErrors && redoingInV8ObjectInsertionOrder); if (!related) { if (reportErrors) { - reportError(Diagnostics.Property_0_is_incompatible_with_index_signature, symbolToString(prop)); + // For consistency, if we report errors we make sure to report the first error in V8's object insertion order. + if (!redoingInV8ObjectInsertionOrder) { + const related = eachPropertyRelatedTo(source, target, kind, reportErrors, /*redoingInV8ObjectInsertionOrder*/ true); + Debug.assert(related === Ternary.False); + } + else { + reportError(Diagnostics.Property_0_is_incompatible_with_index_signature, symbolToString(prop)); + } } return Ternary.False; } @@ -7492,11 +7502,11 @@ namespace ts { } function transformTypeOfMembers(type: Type, f: (propertyType: Type) => Type) { - const members = createMap(); + const members = new StringMap(); for (const property of getPropertiesOfObjectType(type)) { const original = getTypeOfSymbol(property); const updated = f(original); - members[property.name] = updated === original ? property : createTransientSymbol(property, updated); + members.set(property.name, updated === original ? property : createTransientSymbol(property, updated)); }; return members; } @@ -7714,7 +7724,7 @@ namespace ts { let targetStack: Type[]; let depth = 0; let inferiority = 0; - const visited = createMap(); + const visited = new StringSet(); inferFromTypes(originalSource, originalTarget); function isInProcess(source: Type, target: Type) { @@ -7850,10 +7860,10 @@ namespace ts { return; } const key = source.id + "," + target.id; - if (visited[key]) { + if (visited.has(key)) { return; } - visited[key] = true; + visited.add(key); if (depth === 0) { sourceStack = []; targetStack = []; @@ -8204,7 +8214,7 @@ namespace ts { // check. This gives us a quicker out in the common case where an object type is not a function. const resolved = resolveStructuredTypeMembers(type); return !!(resolved.callSignatures.length || resolved.constructSignatures.length || - resolved.members["bind"] && isTypeSubtypeOf(type, globalFunctionType)); + resolved.members.get("bind") && isTypeSubtypeOf(type, globalFunctionType)); } function getTypeFacts(type: Type): TypeFacts { @@ -8617,12 +8627,13 @@ namespace ts { // If we have previously computed the control flow type for the reference at // this flow loop junction, return the cached type. const id = getFlowNodeId(flow); - const cache = flowLoopCaches[id] || (flowLoopCaches[id] = createMap()); + const cache = flowLoopCaches[id] || (flowLoopCaches[id] = new StringMap()); if (!key) { key = getFlowCacheKey(reference); } - if (cache[key]) { - return cache[key]; + const cached = cache.get(key); + if (cached) { + return cached; } // If this flow loop junction and reference are already being processed, return // the union of the types computed for each branch so far, marked as incomplete. @@ -8652,8 +8663,9 @@ namespace ts { // If we see a value appear in the cache it is a sign that control flow analysis // was restarted and completed by checkExpressionCached. We can simply pick up // the resulting type and bail out. - if (cache[key]) { - return cache[key]; + const cached = cache.get(key); + if (cached) { + return cached; } if (!contains(antecedentTypes, type)) { antecedentTypes.push(type); @@ -8677,7 +8689,7 @@ namespace ts { if (isIncomplete(firstAntecedentType)) { return createFlowType(result, /*incomplete*/ true); } - return cache[key] = result; + return setAndReturn(cache, key, result); } function isMatchingReferenceDiscriminant(expr: Expression) { @@ -8800,14 +8812,14 @@ namespace ts { // We narrow a non-union type to an exact primitive type if the non-union type // is a supertype of that primitive type. For example, type 'any' can be narrowed // to one of the primitive types. - const targetType = typeofTypesByName[literal.text]; + const targetType = typeofTypesByName.get(literal.text); if (targetType && isTypeSubtypeOf(targetType, type)) { return targetType; } } const facts = assumeTrue ? - typeofEQFacts[literal.text] || TypeFacts.TypeofEQHostObject : - typeofNEFacts[literal.text] || TypeFacts.TypeofNEHostObject; + typeofEQFacts.get(literal.text) || TypeFacts.TypeofEQHostObject : + typeofNEFacts.get(literal.text) || TypeFacts.TypeofNEHostObject; return getTypeWithFacts(type, facts); } @@ -10293,7 +10305,7 @@ namespace ts { // Grammar checking checkGrammarObjectLiteralExpression(node, inDestructuringPattern); - const propertiesTable = createMap(); + const propertiesTable = new StringMap(); const propertiesArray: Symbol[] = []; const contextualType = getApparentTypeOfContextualType(node); const contextualTypeHasPattern = contextualType && contextualType.pattern && @@ -10376,7 +10388,7 @@ namespace ts { } } else { - propertiesTable[member.name] = member; + propertiesTable.set(member.name, member); } propertiesArray.push(member); } @@ -10385,12 +10397,12 @@ namespace ts { // type with those properties for which the binding pattern specifies a default value. if (contextualTypeHasPattern) { for (const prop of getPropertiesOfType(contextualType)) { - if (!propertiesTable[prop.name]) { + if (!propertiesTable.get(prop.name)) { if (!(prop.flags & SymbolFlags.Optional)) { error(prop.valueDeclaration || (prop).bindingElement, Diagnostics.Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value); } - propertiesTable[prop.name] = prop; + propertiesTable.set(prop.name, prop); propertiesArray.push(prop); } } @@ -10466,7 +10478,7 @@ namespace ts { } } - function checkJsxAttribute(node: JsxAttribute, elementAttributesType: Type, nameTable: Map) { + function checkJsxAttribute(node: JsxAttribute, elementAttributesType: Type, nameTable: Set) { let correspondingPropType: Type = undefined; // Look up the corresponding property for this attribute @@ -10505,34 +10517,35 @@ namespace ts { checkTypeAssignableTo(exprType, correspondingPropType, node); } - nameTable[node.name.text] = true; + nameTable.add(node.name.text); return exprType; } - function checkJsxSpreadAttribute(node: JsxSpreadAttribute, elementAttributesType: Type, nameTable: Map) { + function checkJsxSpreadAttribute(node: JsxSpreadAttribute, elementAttributesType: Type, nameTable: Set) { const type = checkExpression(node.expression); const props = getPropertiesOfType(type); for (const prop of props) { // Is there a corresponding property in the element attributes type? Skip checking of properties // that have already been assigned to, as these are not actually pushed into the resulting type - if (!nameTable[prop.name]) { + if (!nameTable.has(prop.name)) { const targetPropSym = getPropertyOfType(elementAttributesType, prop.name); if (targetPropSym) { const msg = chainDiagnosticMessages(undefined, Diagnostics.Property_0_of_JSX_spread_attribute_is_not_assignable_to_target_property, prop.name); checkTypeAssignableTo(getTypeOfSymbol(prop), getTypeOfSymbol(targetPropSym), node, undefined, msg); } - nameTable[prop.name] = true; + nameTable.add(prop.name); } } return type; } function getJsxType(name: string) { - if (jsxTypes[name] === undefined) { - return jsxTypes[name] = getExportedTypeFromNamespace(JsxNames.JSX, name) || unknownType; + const jsxType = jsxTypes.get(name); + if (jsxType === undefined) { + return setAndReturn(jsxTypes, name, getExportedTypeFromNamespace(JsxNames.JSX, name) || unknownType); } - return jsxTypes[name]; + return jsxType; } /** @@ -10837,7 +10850,7 @@ namespace ts { const targetAttributesType = getJsxElementAttributesType(node); - const nameTable = createMap(); + const nameTable = new StringSet(); // Process this array in right-to-left order so we know which // attributes (mostly from spreads) are being overwritten and // thus should have their types ignored @@ -10861,7 +10874,7 @@ namespace ts { const targetProperties = getPropertiesOfType(targetAttributesType); for (let i = 0; i < targetProperties.length; i++) { if (!(targetProperties[i].flags & SymbolFlags.Optional) && - !nameTable[targetProperties[i].name]) { + !nameTable.has(targetProperties[i].name)) { error(node, Diagnostics.Property_0_is_missing_in_type_1, targetProperties[i].name, typeToString(targetAttributesType)); } @@ -11591,7 +11604,7 @@ namespace ts { return typeArgumentsAreAssignable; } - function checkApplicableSignature(node: CallLikeExpression, args: Expression[], signature: Signature, relation: Map, excludeArgument: boolean[], reportErrors: boolean) { + function checkApplicableSignature(node: CallLikeExpression, args: Expression[], signature: Signature, relation: Map, excludeArgument: boolean[], reportErrors: boolean) { const thisType = getThisTypeOfSignature(signature); if (thisType && thisType !== voidType && node.kind !== SyntaxKind.NewExpression) { // If the called expression is not of the form `x.f` or `x["f"]`, then sourceType = voidType @@ -12125,7 +12138,7 @@ namespace ts { diagnostics.add(createDiagnosticForNodeFromMessageChain(node, errorInfo)); } - function chooseOverload(candidates: Signature[], relation: Map, signatureHelpTrailingComma = false) { + function chooseOverload(candidates: Signature[], relation: Map, signatureHelpTrailingComma = false) { for (const originalCandidate of candidates) { if (!hasCorrectArity(node, args, originalCandidate, signatureHelpTrailingComma)) { continue; @@ -14271,8 +14284,8 @@ namespace ts { Property = Getter | Setter } - const instanceNames = createMap(); - const staticNames = createMap(); + const instanceNames = new StringMap(); + const staticNames = new StringMap(); for (const member of node.members) { if (member.kind === SyntaxKind.Constructor) { for (const param of (member as ConstructorDeclaration).parameters) { @@ -14304,24 +14317,24 @@ namespace ts { } } - function addName(names: Map, location: Node, name: string, meaning: Accessor) { - const prev = names[name]; + function addName(names: Map, location: Node, name: string, meaning: Accessor) { + const prev = names.get(name); if (prev) { if (prev & meaning) { error(location, Diagnostics.Duplicate_identifier_0, getTextOfNode(location)); } else { - names[name] = prev | meaning; + names.set(name, prev | meaning); } } else { - names[name] = meaning; + names.set(name, meaning); } } } function checkObjectTypeForDuplicateDeclarations(node: TypeLiteralNode | InterfaceDeclaration) { - const names = createMap(); + const names = new StringSet(); for (const member of node.members) { if (member.kind == SyntaxKind.PropertySignature) { let memberName: string; @@ -14335,12 +14348,12 @@ namespace ts { continue; } - if (names[memberName]) { + if (names.has(memberName)) { error(member.symbol.valueDeclaration.name, Diagnostics.Duplicate_identifier_0, memberName); error(member.name, Diagnostics.Duplicate_identifier_0, memberName); } else { - names[memberName] = true; + names.add(memberName); } } } @@ -15545,8 +15558,7 @@ namespace ts { function checkUnusedLocalsAndParameters(node: Node): void { if (node.parent.kind !== SyntaxKind.InterfaceDeclaration && noUnusedIdentifiers && !isInAmbientContext(node)) { - for (const key in node.locals) { - const local = node.locals[key]; + node.locals.forEach(local => { if (!local.isReferenced) { if (local.valueDeclaration && local.valueDeclaration.kind === SyntaxKind.Parameter) { const parameter = local.valueDeclaration; @@ -15561,7 +15573,7 @@ namespace ts { forEach(local.declarations, d => error(d.name || d, Diagnostics._0_is_declared_but_never_used, local.name)); } } - } + }); } } @@ -15611,8 +15623,7 @@ namespace ts { function checkUnusedModuleMembers(node: ModuleDeclaration | SourceFile): void { if (compilerOptions.noUnusedLocals && !isInAmbientContext(node)) { - for (const key in node.locals) { - const local = node.locals[key]; + node.locals.forEach(local => { if (!local.isReferenced && !local.exportSymbol) { for (const declaration of local.declarations) { if (!isAmbientModule(declaration)) { @@ -15620,7 +15631,7 @@ namespace ts { } } } - } + }); } } @@ -16637,7 +16648,7 @@ namespace ts { const identifierName = (catchClause.variableDeclaration.name).text; const locals = catchClause.block.locals; if (locals) { - const localSymbol = locals[identifierName]; + const localSymbol = locals.get(identifierName); if (localSymbol && (localSymbol.flags & SymbolFlags.BlockScopedVariable) !== 0) { grammarErrorOnNode(localSymbol.valueDeclaration, Diagnostics.Cannot_redeclare_identifier_0_in_catch_clause, identifierName); } @@ -17057,16 +17068,15 @@ namespace ts { return true; } - const seen = createMap<{ prop: Symbol; containingType: Type }>(); - forEach(resolveDeclaredMembers(type).declaredProperties, p => { seen[p.name] = { prop: p, containingType: type }; }); + const seen: Map = arrayToMap(resolveDeclaredMembers(type).declaredProperties, p => p.name, p => ({ prop: p, containingType: type })); let ok = true; for (const base of baseTypes) { const properties = getPropertiesOfObjectType(getTypeWithThisArgument(base, type.thisType)); for (const prop of properties) { - const existing = seen[prop.name]; + const existing = seen.get(prop.name); if (!existing) { - seen[prop.name] = { prop: prop, containingType: base }; + seen.set(prop.name, { prop, containingType: base }); } else { const isInheritedProperty = existing.containingType !== type; @@ -17804,19 +17814,14 @@ namespace ts { } function hasExportedMembers(moduleSymbol: Symbol) { - for (const id in moduleSymbol.exports) { - if (id !== "export=") { - return true; - } - } - return false; + return someKeyInMap(moduleSymbol.exports, id => id !== "export="); } function checkExternalModuleExports(node: SourceFile | ModuleDeclaration) { const moduleSymbol = getSymbolOfNode(node); const links = getSymbolLinks(moduleSymbol); if (!links.exportsChecked) { - const exportEqualsSymbol = moduleSymbol.exports["export="]; + const exportEqualsSymbol = moduleSymbol.exports.get("export="); if (exportEqualsSymbol && hasExportedMembers(moduleSymbol)) { const declaration = getDeclarationOfAliasSymbol(exportEqualsSymbol) || exportEqualsSymbol.valueDeclaration; if (!isTopLevelInExternalModuleAugmentation(declaration)) { @@ -17825,21 +17830,20 @@ namespace ts { } // Checks for export * conflicts const exports = getExportsOfModule(moduleSymbol); - for (const id in exports) { + exports.forEach(({ declarations, flags }, id) => { if (id === "__export") { - continue; + return; } - const { declarations, flags } = exports[id]; // ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries. // (TS Exceptions: namespaces, function overloads, enums, and interfaces) if (flags & (SymbolFlags.Namespace | SymbolFlags.Interface | SymbolFlags.Enum)) { - continue; + return; } const exportedDeclarationsCount = countWhere(declarations, isNotOverload); if (flags & SymbolFlags.TypeAlias && exportedDeclarationsCount <= 2) { // it is legal to merge type alias with other values // so count should be either 1 (just type alias) or 2 (type alias + merged value) - continue; + return; } if (exportedDeclarationsCount > 1) { for (const declaration of declarations) { @@ -17848,7 +17852,7 @@ namespace ts { } } } - } + }); links.exportsChecked = true; } @@ -18123,7 +18127,7 @@ namespace ts { } function getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[] { - const symbols = createMap(); + const symbols = new StringMap(); let memberFlags: ModifierFlags = ModifierFlags.None; if (isInsideWithStatementBody(location)) { @@ -18201,18 +18205,17 @@ namespace ts { // We will copy all symbol regardless of its reserved name because // symbolsToArray will check whether the key is a reserved name and // it will not copy symbol with reserved name to the array - if (!symbols[id]) { - symbols[id] = symbol; + if (!symbols.get(id)) { + symbols.set(id, symbol); } } } function copySymbols(source: SymbolTable, meaning: SymbolFlags): void { if (meaning) { - for (const id in source) { - const symbol = source[id]; + source.forEach((symbol, id) => { copySymbol(symbol, meaning); - } + }); } } } @@ -18623,8 +18626,8 @@ namespace ts { const propsByName = createSymbolTable(getPropertiesOfType(type)); if (getSignaturesOfType(type, SignatureKind.Call).length || getSignaturesOfType(type, SignatureKind.Construct).length) { forEach(getPropertiesOfType(globalFunctionType), p => { - if (!propsByName[p.name]) { - propsByName[p.name] = p; + if (!propsByName.get(p.name)) { + propsByName.set(p.name, p); } }); } @@ -18687,7 +18690,7 @@ namespace ts { // otherwise - check if at least one export is value symbolLinks.exportsSomeValue = hasExportAssignment ? !!(moduleSymbol.flags & SymbolFlags.Value) - : forEachProperty(getExportsOfModule(moduleSymbol), isValue); + : someValueInMap(getExportsOfModule(moduleSymbol), isValue); } return symbolLinks.exportsSomeValue; @@ -19037,7 +19040,7 @@ namespace ts { } function hasGlobalName(name: string): boolean { - return !!globals[name]; + return !!globals.get(name); } function getReferencedValueSymbol(reference: Identifier, startInDeclarationContainer?: boolean): Symbol { @@ -19094,14 +19097,13 @@ namespace ts { if (resolvedTypeReferenceDirectives) { // populate reverse mapping: file path -> type reference directive that was resolved to this file fileToDirective = createFileMap(); - for (const key in resolvedTypeReferenceDirectives) { - const resolvedDirective = resolvedTypeReferenceDirectives[key]; + resolvedTypeReferenceDirectives.forEach((resolvedDirective, key) => { if (!resolvedDirective) { - continue; + return; } const file = host.getSourceFile(resolvedDirective.resolvedFileName); fileToDirective.set(file.path, key); - } + }); } return { getReferencedExportContainer, @@ -19242,11 +19244,9 @@ namespace ts { if (file.symbol && file.symbol.globalExports) { // Merge in UMD exports with first-in-wins semantics (see #9771) const source = file.symbol.globalExports; - for (const id in source) { - if (!(id in globals)) { - globals[id] = source[id]; - } - } + source.forEach((sourceSymbol, id) => { + setIfNotSet(globals, id, sourceSymbol); + }); } if ((compilerOptions.isolatedModules || isExternalModule(file)) && !file.isDeclarationFile) { const fileRequestedExternalEmitHelpers = file.flags & NodeFlags.EmitHelperFlags; @@ -19943,7 +19943,7 @@ namespace ts { } function checkGrammarObjectLiteralExpression(node: ObjectLiteralExpression, inDestructuring: boolean) { - const seen = createMap(); + const seen = new StringMap(); const Property = 1; const GetAccessor = 2; const SetAccessor = 4; @@ -20007,17 +20007,17 @@ namespace ts { continue; } - if (!seen[effectiveName]) { - seen[effectiveName] = currentKind; + const existingKind = seen.get(effectiveName); + if (!existingKind) { + seen.set(effectiveName, currentKind); } else { - const existingKind = seen[effectiveName]; if (currentKind === Property && existingKind === Property) { grammarErrorOnNode(name, Diagnostics.Duplicate_identifier_0, getTextOfNode(name)); } else if ((currentKind & GetOrSetAccessor) && (existingKind & GetOrSetAccessor)) { if (existingKind !== GetOrSetAccessor && currentKind !== existingKind) { - seen[effectiveName] = currentKind | existingKind; + seen.set(effectiveName, currentKind | existingKind); } else { return grammarErrorOnNode(name, Diagnostics.An_object_literal_cannot_have_multiple_get_Slashset_accessors_with_the_same_name); @@ -20031,7 +20031,7 @@ namespace ts { } function checkGrammarJsxElement(node: JsxOpeningLikeElement) { - const seen = createMap(); + const seen = new StringSet(); for (const attr of node.attributes) { if (attr.kind === SyntaxKind.JsxSpreadAttribute) { continue; @@ -20039,8 +20039,8 @@ namespace ts { const jsxAttr = (attr); const name = jsxAttr.name; - if (!seen[name.text]) { - seen[name.text] = true; + if (!seen.has(name.text)) { + seen.add(name.text); } else { return grammarErrorOnNode(name, Diagnostics.JSX_elements_cannot_have_multiple_attributes_with_the_same_name); @@ -20534,11 +20534,11 @@ namespace ts { function getAmbientModules(): Symbol[] { const result: Symbol[] = []; - for (const sym in globals) { + globals.forEach((global, sym) => { if (ambientModuleSymbolRegex.test(sym)) { - result.push(globals[sym]); + result.push(global); } - } + }); return result; } } diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 648447ac26ac4..12e36f7534a7e 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -65,7 +65,7 @@ namespace ts { }, { name: "jsx", - type: createMap({ + type: mapOfMapLike({ "preserve": JsxEmit.Preserve, "react": JsxEmit.React }), @@ -95,7 +95,7 @@ namespace ts { { name: "module", shortName: "m", - type: createMap({ + type: mapOfMapLike({ "none": ModuleKind.None, "commonjs": ModuleKind.CommonJS, "amd": ModuleKind.AMD, @@ -109,7 +109,7 @@ namespace ts { }, { name: "newLine", - type: createMap({ + type: mapOfMapLike({ "crlf": NewLineKind.CarriageReturnLineFeed, "lf": NewLineKind.LineFeed }), @@ -258,7 +258,7 @@ namespace ts { { name: "target", shortName: "t", - type: createMap({ + type: mapOfMapLike({ "es3": ScriptTarget.ES3, "es5": ScriptTarget.ES5, "es6": ScriptTarget.ES6, @@ -292,7 +292,7 @@ namespace ts { }, { name: "moduleResolution", - type: createMap({ + type: mapOfMapLike({ "node": ModuleResolutionKind.NodeJs, "classic": ModuleResolutionKind.Classic, }), @@ -401,7 +401,7 @@ namespace ts { type: "list", element: { name: "lib", - type: createMap({ + type: mapOfMapLike({ // JavaScript only "es5": "lib.es5.d.ts", "es6": "lib.es2015.d.ts", @@ -473,8 +473,8 @@ namespace ts { /* @internal */ export interface OptionNameMap { - optionNameMap: Map; - shortOptionNames: Map; + optionNameMap: Map; + shortOptionNames: Map; } /* @internal */ @@ -493,12 +493,12 @@ namespace ts { return optionNameMapCache; } - const optionNameMap = createMap(); - const shortOptionNames = createMap(); + const optionNameMap = new StringMap(); + const shortOptionNames = new StringMap(); forEach(optionDeclarations, option => { - optionNameMap[option.name.toLowerCase()] = option; + optionNameMap.set(option.name.toLowerCase(), option); if (option.shortName) { - shortOptionNames[option.shortName] = option.name; + shortOptionNames.set(option.shortName, option.name); } }); @@ -509,18 +509,16 @@ namespace ts { /* @internal */ export function createCompilerDiagnosticForInvalidCustomType(opt: CommandLineOptionOfCustomType): Diagnostic { const namesOfType: string[] = []; - for (const key in opt.type) { - namesOfType.push(` '${key}'`); - } + forEachKeyInMap(opt.type, key => namesOfType.push(` '${key}'`)); return createCompilerDiagnostic(Diagnostics.Argument_for_0_option_must_be_Colon_1, `--${opt.name}`, namesOfType); } /* @internal */ export function parseCustomTypeOption(opt: CommandLineOptionOfCustomType, value: string, errors: Diagnostic[]) { const key = trimString((value || "")).toLowerCase(); - const map = opt.type; - if (key in map) { - return map[key]; + const customType = opt.type.get(key); + if (customType !== undefined) { + return customType; } else { errors.push(createCompilerDiagnosticForInvalidCustomType(opt)); @@ -573,13 +571,13 @@ namespace ts { s = s.slice(s.charCodeAt(1) === CharacterCodes.minus ? 2 : 1).toLowerCase(); // Try to translate short option names to their full equivalents. - if (s in shortOptionNames) { - s = shortOptionNames[s]; + const short = shortOptionNames.get(s); + if (short !== undefined) { + s = short; } - if (s in optionNameMap) { - const opt = optionNameMap[s]; - + const opt = optionNameMap.get(s); + if (opt !== undefined) { if (opt.isTSConfigOnly) { errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file, opt.name)); } @@ -696,7 +694,7 @@ namespace ts { * @param fileNames array of filenames to be generated into tsconfig.json */ /* @internal */ - export function generateTSConfig(options: CompilerOptions, fileNames: string[]): { compilerOptions: Map } { + export function generateTSConfig(options: CompilerOptions, fileNames: string[]): { compilerOptions: MapLike } { const compilerOptions = extend(options, defaultInitCompilerOptions); const configurations: any = { compilerOptions: serializeCompilerOptions(compilerOptions) @@ -708,7 +706,7 @@ namespace ts { return configurations; - function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): Map | undefined { + function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): Map | undefined { if (optionDefinition.type === "string" || optionDefinition.type === "number" || optionDefinition.type === "boolean") { // this is of a type CommandLineOptionOfPrimitiveType return undefined; @@ -721,18 +719,17 @@ namespace ts { } } - function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: MapLike): string | undefined { + function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: Map): string | undefined { // There is a typeMap associated with this command-line option so use it to map value back to its name - for (const key in customTypeMap) { - if (customTypeMap[key] === value) { + return findInMap(customTypeMap, (customValue, key) => { + if (customValue === value) { return key; } - } - return undefined; + }); } - function serializeCompilerOptions(options: CompilerOptions): Map { - const result = createMap(); + function serializeCompilerOptions(options: CompilerOptions): MapLike { + const result = new StringMap(); const optionsNameMap = getOptionNameMap().optionNameMap; for (const name in options) { @@ -748,13 +745,13 @@ namespace ts { break; default: const value = options[name]; - let optionDefinition = optionsNameMap[name.toLowerCase()]; + let optionDefinition = optionsNameMap.get(name.toLowerCase()); if (optionDefinition) { const customTypeMap = getCustomTypeMapOfCommandLineOption(optionDefinition); if (!customTypeMap) { // There is no map associated with this compiler option then use the value as-is // This is the case if the value is expect to be string, number, boolean or list of string - result[name] = value; + result.set(name, value); } else { if (optionDefinition.type === "list") { @@ -762,11 +759,11 @@ namespace ts { for (const element of value as (string | number)[]) { convertedValue.push(getNameOfCompilerOptionValue(element, customTypeMap)); } - result[name] = convertedValue; + result.set(name, convertedValue); } else { // There is a typeMap associated with this command-line option so use it to map value back to its name - result[name] = getNameOfCompilerOptionValue(value, customTypeMap); + result.set(name, getNameOfCompilerOptionValue(value, customTypeMap)); } } } @@ -774,7 +771,7 @@ namespace ts { } } } - return result; + return mapLikeOfMap(result); } } @@ -1001,8 +998,8 @@ namespace ts { const optionNameMap = arrayToMap(optionDeclarations, opt => opt.name); for (const id in jsonOptions) { - if (id in optionNameMap) { - const opt = optionNameMap[id]; + const opt = optionNameMap.get(id); + if (opt !== undefined) { defaultOptions[opt.name] = convertJsonOption(opt, jsonOptions[id], basePath, errors); } else { @@ -1038,8 +1035,9 @@ namespace ts { function convertJsonOptionOfCustomType(opt: CommandLineOptionOfCustomType, value: string, errors: Diagnostic[]) { const key = value.toLowerCase(); - if (key in opt.type) { - return opt.type[key]; + const val = opt.type.get(key); + if (val !== undefined) { + return val; } else { errors.push(createCompilerDiagnosticForInvalidCustomType(opt)); @@ -1148,12 +1146,12 @@ namespace ts { // Literal file names (provided via the "files" array in tsconfig.json) are stored in a // file map with a possibly case insensitive key. We use this map later when when including // wildcard paths. - const literalFileMap = createMap(); + const literalFileMap = new StringMap(); // Wildcard paths (provided via the "includes" array in tsconfig.json) are stored in a // file map with a possibly case insensitive key. We use this map to store paths matched // via wildcard, and to handle extension priority. - const wildcardFileMap = createMap(); + const wildcardFileMap = new StringMap(); if (include) { include = validateSpecs(include, errors, /*allowTrailingRecursion*/ false); @@ -1167,7 +1165,7 @@ namespace ts { // file map that marks whether it was a regular wildcard match (with a `*` or `?` token), // or a recursive directory. This information is used by filesystem watchers to monitor for // new entries in these paths. - const wildcardDirectories: Map = getWildcardDirectories(include, exclude, basePath, host.useCaseSensitiveFileNames); + const wildcardDirectories: Map = getWildcardDirectories(include, exclude, basePath, host.useCaseSensitiveFileNames); // Rather than requery this for each file and filespec, we query the supported extensions // once and store it on the expansion context. @@ -1178,7 +1176,7 @@ namespace ts { if (fileNames) { for (const fileName of fileNames) { const file = combinePaths(basePath, fileName); - literalFileMap[keyMapper(file)] = file; + literalFileMap.set(keyMapper(file), file); } } @@ -1201,18 +1199,17 @@ namespace ts { removeWildcardFilesWithLowerPriorityExtension(file, wildcardFileMap, supportedExtensions, keyMapper); const key = keyMapper(file); - if (!(key in literalFileMap) && !(key in wildcardFileMap)) { - wildcardFileMap[key] = file; + if (!literalFileMap.has(key)) { + setIfNotSet(wildcardFileMap, key, file); } } } - const literalFiles = reduceProperties(literalFileMap, addFileToOutput, []); - const wildcardFiles = reduceProperties(wildcardFileMap, addFileToOutput, []); - wildcardFiles.sort(host.useCaseSensitiveFileNames ? compareStrings : compareStringsCaseInsensitive); + const literalFiles = valuesOfMap(literalFileMap); + const wildcardFiles = valuesOfMap(wildcardFileMap).sort(host.useCaseSensitiveFileNames ? compareStrings : compareStringsCaseInsensitive); return { fileNames: literalFiles.concat(wildcardFiles), - wildcardDirectories + wildcardDirectories: mapLikeOfMap(wildcardDirectories) }; } @@ -1253,7 +1250,7 @@ namespace ts { // /a/b/a?z - Watch /a/b directly to catch any new file matching a?z const rawExcludeRegex = getRegularExpressionForWildcard(exclude, path, "exclude"); const excludeRegex = rawExcludeRegex && new RegExp(rawExcludeRegex, useCaseSensitiveFileNames ? "" : "i"); - const wildcardDirectories = createMap(); + const wildcardDirectories = new StringMap(); if (include !== undefined) { const recursiveKeys: string[] = []; for (const file of include) { @@ -1266,9 +1263,9 @@ namespace ts { if (match) { const key = useCaseSensitiveFileNames ? match[0] : match[0].toLowerCase(); const flags = watchRecursivePattern.test(name) ? WatchDirectoryFlags.Recursive : WatchDirectoryFlags.None; - const existingFlags = wildcardDirectories[key]; + const existingFlags = wildcardDirectories.get(key); if (existingFlags === undefined || existingFlags < flags) { - wildcardDirectories[key] = flags; + wildcardDirectories.set(key, flags); if (flags === WatchDirectoryFlags.Recursive) { recursiveKeys.push(key); } @@ -1277,13 +1274,13 @@ namespace ts { } // Remove any subpaths under an existing recursively watched directory. - for (const key in wildcardDirectories) { + forEachKeyInMap(wildcardDirectories, key => { for (const recursiveKey of recursiveKeys) { if (key !== recursiveKey && containsPath(recursiveKey, key, path, !useCaseSensitiveFileNames)) { - delete wildcardDirectories[key]; + wildcardDirectories.delete(key); } } - } + }); } return wildcardDirectories; @@ -1297,13 +1294,13 @@ namespace ts { * @param extensionPriority The priority of the extension. * @param context The expansion context. */ - function hasFileWithHigherPriorityExtension(file: string, literalFiles: Map, wildcardFiles: Map, extensions: string[], keyMapper: (value: string) => string) { + function hasFileWithHigherPriorityExtension(file: string, literalFiles: Map, wildcardFiles: Map, extensions: string[], keyMapper: (value: string) => string) { const extensionPriority = getExtensionPriority(file, extensions); const adjustedExtensionPriority = adjustExtensionPriority(extensionPriority); for (let i = ExtensionPriority.Highest; i < adjustedExtensionPriority; i++) { const higherPriorityExtension = extensions[i]; const higherPriorityPath = keyMapper(changeExtension(file, higherPriorityExtension)); - if (higherPriorityPath in literalFiles || higherPriorityPath in wildcardFiles) { + if (literalFiles.has(higherPriorityPath) || wildcardFiles.has(higherPriorityPath)) { return true; } } @@ -1319,27 +1316,16 @@ namespace ts { * @param extensionPriority The priority of the extension. * @param context The expansion context. */ - function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: Map, extensions: string[], keyMapper: (value: string) => string) { + function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: Map, extensions: string[], keyMapper: (value: string) => string) { const extensionPriority = getExtensionPriority(file, extensions); const nextExtensionPriority = getNextLowestExtensionPriority(extensionPriority); for (let i = nextExtensionPriority; i < extensions.length; i++) { const lowerPriorityExtension = extensions[i]; const lowerPriorityPath = keyMapper(changeExtension(file, lowerPriorityExtension)); - delete wildcardFiles[lowerPriorityPath]; + wildcardFiles.delete(lowerPriorityPath); } } - /** - * Adds a file to an array of files. - * - * @param output The output array. - * @param file The file path. - */ - function addFileToOutput(output: string[], file: string) { - output.push(file); - return output; - } - /** * Gets a case sensitive key. * diff --git a/src/compiler/core.ts b/src/compiler/core.ts index e63bcbf8474b4..4a9c8d7f771ee 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1,4 +1,5 @@ -/// +/// +/// /// /* @internal */ @@ -18,28 +19,8 @@ namespace ts { True = -1 } - const createObject = Object.create; - - export function createMap(template?: MapLike): Map { - const map: Map = createObject(null); // tslint:disable-line:no-null-keyword - - // Using 'delete' on an object causes V8 to put the object in dictionary mode. - // This disables creation of hidden classes, which are expensive when an object is - // constantly changing shape. - map["__"] = undefined; - delete map["__"]; - - // Copies keys/values from template. Note that for..in will not throw if - // template is undefined, and instead will just exit the loop. - for (const key in template) if (hasOwnProperty.call(template, key)) { - map[key] = template[key]; - } - - return map; - } - export function createFileMap(keyMapper?: (key: string) => string): FileMap { - let files = createMap(); + const files = new StringMap(); return { get, set, @@ -51,39 +32,33 @@ namespace ts { }; function forEachValueInMap(f: (key: Path, value: T) => void) { - for (const key in files) { - f(key, files[key]); - } + files.forEach((value, key) => f(key as Path, value)); } - function getKeys() { - const keys: Path[] = []; - for (const key in files) { - keys.push(key); - } - return keys; + function getKeys(): Path[] { + return keysOfMap(files) as Path[]; } // path should already be well-formed so it does not need to be normalized function get(path: Path): T { - return files[toKey(path)]; + return files.get(toKey(path)); } function set(path: Path, value: T) { - files[toKey(path)] = value; + files.set(toKey(path), value); } function contains(path: Path) { - return toKey(path) in files; + return files.has(toKey(path)); } function remove(path: Path) { const key = toKey(path); - delete files[key]; + files.delete(key); } function clear() { - files = createMap(); + files.clear(); } function toKey(path: Path): string { @@ -586,242 +561,6 @@ namespace ts { return initial; } - const hasOwnProperty = Object.prototype.hasOwnProperty; - - /** - * Indicates whether a map-like contains an own property with the specified key. - * - * NOTE: This is intended for use only with MapLike objects. For Map objects, use - * the 'in' operator. - * - * @param map A map-like. - * @param key A property key. - */ - export function hasProperty(map: MapLike, key: string): boolean { - return hasOwnProperty.call(map, key); - } - - /** - * Gets the value of an owned property in a map-like. - * - * NOTE: This is intended for use only with MapLike objects. For Map objects, use - * an indexer. - * - * @param map A map-like. - * @param key A property key. - */ - export function getProperty(map: MapLike, key: string): T | undefined { - return hasOwnProperty.call(map, key) ? map[key] : undefined; - } - - /** - * Gets the owned, enumerable property keys of a map-like. - * - * NOTE: This is intended for use with MapLike objects. For Map objects, use - * Object.keys instead as it offers better performance. - * - * @param map A map-like. - */ - export function getOwnKeys(map: MapLike): string[] { - const keys: string[] = []; - for (const key in map) if (hasOwnProperty.call(map, key)) { - keys.push(key); - } - return keys; - } - - /** - * Enumerates the properties of a Map, invoking a callback and returning the first truthy result. - * - * @param map A map for which properties should be enumerated. - * @param callback A callback to invoke for each property. - */ - export function forEachProperty(map: Map, callback: (value: T, key: string) => U): U { - let result: U; - for (const key in map) { - if (result = callback(map[key], key)) break; - } - return result; - } - - /** - * Returns true if a Map has some matching property. - * - * @param map A map whose properties should be tested. - * @param predicate An optional callback used to test each property. - */ - export function someProperties(map: Map, predicate?: (value: T, key: string) => boolean) { - for (const key in map) { - if (!predicate || predicate(map[key], key)) return true; - } - return false; - } - - /** - * Performs a shallow copy of the properties from a source Map to a target MapLike - * - * @param source A map from which properties should be copied. - * @param target A map to which properties should be copied. - */ - export function copyProperties(source: Map, target: MapLike): void { - for (const key in source) { - target[key] = source[key]; - } - } - - export function assign, T2, T3>(t: T1, arg1: T2, arg2: T3): T1 & T2 & T3; - export function assign, T2>(t: T1, arg1: T2): T1 & T2; - export function assign>(t: T1, ...args: any[]): any; - export function assign>(t: T1, ...args: any[]) { - for (const arg of args) { - for (const p of getOwnKeys(arg)) { - t[p] = arg[p]; - } - } - return t; - } - - /** - * Reduce the properties of a map. - * - * NOTE: This is intended for use with Map objects. For MapLike objects, use - * reduceOwnProperties instead as it offers better runtime safety. - * - * @param map The map to reduce - * @param callback An aggregation function that is called for each entry in the map - * @param initial The initial value for the reduction. - */ - export function reduceProperties(map: Map, callback: (aggregate: U, value: T, key: string) => U, initial: U): U { - let result = initial; - for (const key in map) { - result = callback(result, map[key], String(key)); - } - return result; - } - - /** - * Reduce the properties defined on a map-like (but not from its prototype chain). - * - * NOTE: This is intended for use with MapLike objects. For Map objects, use - * reduceProperties instead as it offers better performance. - * - * @param map The map-like to reduce - * @param callback An aggregation function that is called for each entry in the map - * @param initial The initial value for the reduction. - */ - export function reduceOwnProperties(map: MapLike, callback: (aggregate: U, value: T, key: string) => U, initial: U): U { - let result = initial; - for (const key in map) if (hasOwnProperty.call(map, key)) { - result = callback(result, map[key], String(key)); - } - return result; - } - - /** - * Performs a shallow equality comparison of the contents of two map-likes. - * - * @param left A map-like whose properties should be compared. - * @param right A map-like whose properties should be compared. - */ - export function equalOwnProperties(left: MapLike, right: MapLike, equalityComparer?: (left: T, right: T) => boolean) { - if (left === right) return true; - if (!left || !right) return false; - for (const key in left) if (hasOwnProperty.call(left, key)) { - if (!hasOwnProperty.call(right, key) === undefined) return false; - if (equalityComparer ? !equalityComparer(left[key], right[key]) : left[key] !== right[key]) return false; - } - for (const key in right) if (hasOwnProperty.call(right, key)) { - if (!hasOwnProperty.call(left, key)) return false; - } - return true; - } - - /** - * Creates a map from the elements of an array. - * - * @param array the array of input elements. - * @param makeKey a function that produces a key for a given element. - * - * This function makes no effort to avoid collisions; if any two elements produce - * the same key with the given 'makeKey' function, then the element with the higher - * index in the array will be the one associated with the produced key. - */ - export function arrayToMap(array: T[], makeKey: (value: T) => string): Map; - export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue: (value: T) => U): Map; - export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue?: (value: T) => U): Map { - const result = createMap(); - for (const value of array) { - result[makeKey(value)] = makeValue ? makeValue(value) : value; - } - return result; - } - - export function isEmpty(map: Map) { - for (const id in map) { - if (hasProperty(map, id)) { - return false; - } - } - return true; - } - - export function cloneMap(map: Map) { - const clone = createMap(); - copyProperties(map, clone); - return clone; - } - - export function clone(object: T): T { - const result: any = {}; - for (const id in object) { - if (hasOwnProperty.call(object, id)) { - result[id] = (object)[id]; - } - } - return result; - } - - export function extend(first: T1 , second: T2): T1 & T2 { - const result: T1 & T2 = {}; - for (const id in second) if (hasOwnProperty.call(second, id)) { - (result as any)[id] = (second as any)[id]; - } - for (const id in first) if (hasOwnProperty.call(first, id)) { - (result as any)[id] = (first as any)[id]; - } - return result; - } - - /** - * Adds the value to an array of values associated with the key, and returns the array. - * Creates the array if it does not already exist. - */ - export function multiMapAdd(map: Map, key: string, value: V): V[] { - const values = map[key]; - if (values) { - values.push(value); - return values; - } - else { - return map[key] = [value]; - } - } - - /** - * Removes a value from an array of values associated with the key. - * Does not preserve the order of those values. - * Does nothing if `key` is not in `map`, or `value` is not in `map[key]`. - */ - export function multiMapRemove(map: Map, key: string, value: V): void { - const values = map[key]; - if (values) { - unorderedRemoveItem(values, value); - if (!values.length) { - delete map[key]; - } - } - } - /** * Tests whether a value is an array. */ @@ -912,10 +651,10 @@ namespace ts { return text.replace(/{(\d+)}/g, (match, index?) => args[+index + baseIndex]); } - export let localizedDiagnosticMessages: Map = undefined; + export let localizedDiagnosticMessages: Map = undefined; export function getLocaleSpecificMessage(message: DiagnosticMessage) { - return localizedDiagnosticMessages && localizedDiagnosticMessages[message.key] || message.message; + return localizedDiagnosticMessages && localizedDiagnosticMessages.get(message.key) || message.message; } export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage, ...args: any[]): Diagnostic; diff --git a/src/compiler/dataStructures.ts b/src/compiler/dataStructures.ts new file mode 100644 index 0000000000000..57874c0f6c1ba --- /dev/null +++ b/src/compiler/dataStructures.ts @@ -0,0 +1,624 @@ +// NumberMap, StringMap, and StringSet shims +/* @internal */ +namespace ts { + // The global Map object. This may not be available, so we must test for it. + declare const Map: NumberMapConstructor & StringMapConstructor | undefined; + const usingNativeMaps = typeof Map !== "undefined"; + // tslint:disable-next-line:no-in-operator + const fullyFeaturedMaps = usingNativeMaps && "keys" in Map.prototype && "values" in Map.prototype && "entries" in Map.prototype; + + /** Extra Map methods that may not be available, so we must provide fallbacks. */ + interface FullyFeaturedMap extends Map { + keys(): Iterator; + values(): Iterator; + entries(): Iterator<[K, V]>; + } + + /** Simplified ES6 Iterator interface. */ + interface Iterator { + next(): { value: T, done: false } | { value: never, done: true }; + } + + export interface NumberMapConstructor { + /** + * Creates a new Map with number keys. + * If `pairs` is provided, each [key, value] pair will be added to the map. + */ + new(pairs?: [K, V][]): Map; + } + + /** + * In runtimes without Maps, this is implemented using a sparse array. + * This is generic over the key type because it is usually an enum. + */ + export const NumberMap: NumberMapConstructor = usingNativeMaps ? Map : class ShimNumberMap implements Map { + private data: { [key: number]: V } = []; + + constructor(pairs?: [K, V][]) { + if (pairs) { + for (const [key, value] of pairs) { + this.data[key as number] = value; + } + } + } + + clear() { + this.data = []; + } + + delete(key: K) { + delete this.data[key as number]; + } + + get(key: K) { + return this.data[key as number]; + } + + has(key: K) { + // tslint:disable-next-line:no-in-operator + return (key as number) in this.data; + } + + set(key: K, value: V) { + this.data[key as number] = value; + } + + forEach(action: (value: V, key: K) => void) { + for (const key in this.data) { + action(this.data[key], key as any as K); + } + } + }; + + export interface StringMapConstructor { + new(): Map; + } + /** In runtimes without Maps, this is implemented using an object. */ + export const StringMap: StringMapConstructor = usingNativeMaps ? Map : class ShimStringMap implements Map { + private data = createDictionaryModeObject(); + + constructor() {} + + clear() { + this.data = createDictionaryModeObject(); + } + + delete(key: string) { + delete this.data[key]; + } + + get(key: string) { + return this.data[key]; + } + + has(key: string) { + // tslint:disable-next-line:no-in-operator + return key in this.data; + } + + set(key: string, value: T) { + this.data[key] = value; + } + + forEach(f: (value: T, key: string) => void) { + for (const key in this.data) { + f(this.data[key], key); + } + } + }; + + const createObject = Object.create; + function createDictionaryModeObject(): MapLike { + const map = createObject(null); // tslint:disable-line:no-null-keyword + + // Using 'delete' on an object causes V8 to put the object in dictionary mode. + // This disables creation of hidden classes, which are expensive when an object is + // constantly changing shape. + map["__"] = undefined; + delete map["__"]; + + return map; + } + + /** Iterates over entries in the map, returning the first output of `getResult` that is not `undefined`. */ + export const findInMap: (map: Map, getResult: (value: V, key: K) => U | undefined) => U | undefined = fullyFeaturedMaps + ? (map: FullyFeaturedMap, f: (value: V, key: K) => U | undefined) => { + const iter = map.entries(); + while (true) { + const { value: pair, done } = iter.next(); + if (done) { + return undefined; + } + const [key, value] = pair; + const result = f(value, key); + if (result !== undefined) { + return result; + } + } + } + : (map: Map, f: (value: V, key: K) => U | undefined) => { + let result: U | undefined; + map.forEach((value, key) => { + if (result === undefined) + result = f(value, key); + }); + return result; + }; + + /** Whether `predicate` is true for at least one entry in the map. */ + export const someInMap: (map: Map, predicate: (value: V, key: K) => boolean) => boolean = fullyFeaturedMaps + ? (map: FullyFeaturedMap, predicate: (value: V, key: K) => boolean) => + someInIterator(map.entries(), ([key, value]) => predicate(value, key)) + : (map: Map, predicate: (value: V, key: K) => boolean) => { + let found = false; + map.forEach((value, key) => { + found = found || predicate(value, key); + }); + return found; + }; + + /** Whether `predicate` is true for at least one key in the map. */ + export const someKeyInMap: (map: Map, predicate: (key: K) => boolean) => boolean = fullyFeaturedMaps + ? (map: FullyFeaturedMap, predicate: (key: K) => boolean) => someInIterator(map.keys(), predicate) + : (map: Map, predicate: (key: K) => boolean) => + someInMap(map, (_value, key) => predicate(key)); + + /** Whether `predicate` is true for at least one value in the map. */ + export const someValueInMap: (map: Map, predicate: (value: T) => boolean) => boolean = fullyFeaturedMaps + ? (map: FullyFeaturedMap, predicate: (value: T) => boolean) => + someInIterator(map.values(), predicate) + : someInMap; + + function someInIterator(iterator: Iterator, predicate: (value: T) => boolean): boolean { + while (true) { + const { value, done } = iterator.next(); + if (done) { + return false; + } + if (predicate(value)) { + return true; + } + } + } + + /** + * Equivalent to the ES6 code: + * `for (const key of map.keys()) action(key);` + */ + export const forEachKeyInMap: (map: Map, action: (key: K) => void) => void = fullyFeaturedMaps + ? (map: FullyFeaturedMap, f: (key: K) => void) => { + const iter: Iterator = map.keys(); + while (true) { + const { value: key, done } = iter.next(); + if (done) { + return; + } + f(key); + } + } + : (map: Map, action: (key: K) => void) => { + map.forEach((_value, key) => action(key)); + }; + + /** Size of a map. */ + export const mapSize: (map: Map) => number = usingNativeMaps + ? map => (map as any).size + : map => { + let size = 0; + map.forEach(() => { size++; }); + return size; + }; + + /** Convert a Map to a MapLike. */ + export function mapLikeOfMap(map: Map): MapLike { + const obj = createDictionaryModeObject(); + map.forEach((value, key) => { + obj[key] = value; + }); + return obj; + } + + /** Create a map from a MapLike. This is useful for writing large maps as object literals. */ + export function mapOfMapLike(object: MapLike): Map { + const map = new StringMap(); + // Copies keys/values from template. Note that for..in will not throw if + // template is undefined, and instead will just exit the loop. + for (const key in object) if (hasProperty(object, key)) { + map.set(key, object[key]); + } + return map; + } + + class ShimStringSet implements Set { + private data = createDictionaryModeObject(); + + constructor() {} + + add(value: string) { + this.data[value] = true; + } + + clear() { + this.data = createDictionaryModeObject(); + } + + delete(value: string) { + delete this.data[value]; + } + + forEach(action: (value: string) => void) { + for (const value in this.data) { + action(value); + } + } + + has(value: string) { + // tslint:disable-next-line:no-in-operator + return value in this.data; + } + + isEmpty() { + // tslint:disable-next-line:no-unused-variable + for (const _ in this.data) { + return false; + } + return true; + } + } + + declare const Set: { new(): Set } | undefined; + const usingNativeSets = typeof Set !== "undefined"; + + /** In runtimes without Sets, this is implemented using an object. */ + export const StringSet: { new(): Set } = usingNativeSets ? Set : ShimStringSet; + + /** False if there are any values in the set. */ + export const setIsEmpty: (set: Set) => boolean = usingNativeSets + ? set => (set as any).size === 0 + : (set: ShimStringSet) => set.isEmpty(); +} + +// Map utilities +namespace ts { + /** Create a map containing a single entry key -> value. */ + export function createMapWithEntry(key: string, value: T): Map { + const map = new StringMap(); + map.set(key, value); + return map; + } + + /** Set a value in a map, then return that value. */ + export function setAndReturn(map: Map, key: K, value: V): V { + map.set(key, value); + return value; + } + + /** False if there are any entries in the map. */ + export function mapIsEmpty(map: Map): boolean { + return !someKeyInMap(map, () => true); + } + + /** Create a new copy of a Map. */ + export function cloneMap(map: Map) { + const clone = new StringMap(); + copyMapEntriesFromTo(map, clone); + return clone; + } + + /** + * Performs a shallow copy of the properties from a source Map to a target Map + * + * @param source A map from which properties should be copied. + * @param target A map to which properties should be copied. + */ + export function copyMapEntriesFromTo(source: Map, target: Map): void { + source.forEach((value, key) => { + target.set(key, value); + }); + } + + /** Equivalent to `Array.from(map.keys())`. */ + export function keysOfMap(map: Map): K[] { + const keys: K[] = []; + forEachKeyInMap(map, key => { keys.push(key); }); + return keys; + } + + /** Equivalent to `Array.from(map.values())`. */ + export function valuesOfMap(map: Map): V[] { + const values: V[] = []; + map.forEach((value) => { values.push(value); }); + return values; + } + + /** Return a new map with each key transformed by `getNewKey`. */ + export function transformKeys(map: Map, getNewKey: (key: string) => string): Map { + const newMap = new StringMap(); + map.forEach((value, key) => { + newMap.set(getNewKey(key), value); + }); + return newMap; + } + + /** Replace each value with the result of calling `getNewValue`. */ + export function updateMapValues(map: Map, getNewValue: (value: V) => V): void { + map.forEach((value, key) => { + map.set(key, getNewValue(value)); + }); + } + + /** + * Change the value at `key` by applying the given function to it. + * If there is no value at `key` then `getNewValue` will be passed `undefined`. + */ + export function modifyValue(map: Map, key: K, getNewValue: (value: V) => V) { + map.set(key, getNewValue(map.get(key))); + } + + /** + * Get a value in the map, or if not already present, set and return it. + * Treats entries set to `undefined` as equivalent to not being set (saving a call to `has`). + */ + export function getOrUpdate(map: Map, key: K, getValue: (key: K) => V): V { + const value = map.get(key); + return value !== undefined ? value : setAndReturn(map, key, getValue(key)); + } + + /** Like `getOrUpdate`, but recognizes `undefined` as having been already set. */ + export function getOrUpdateAndAllowUndefined(map: Map, key: K, getValue: (key: K) => V): V { + return map.has(key) ? map.get(key) : setAndReturn(map, key, getValue(key)); + } + + /** + * Sets the the value if the key is not already in the map. + * Returns whether the value was set. + */ + export function setIfNotSet(map: Map, key: K, value: V): boolean { + const shouldSet = !map.has(key); + if (shouldSet) { + map.set(key, value); + } + return shouldSet; + } + + /** Deletes an entry from a map and returns it; or returns undefined if the key was not in the map. */ + export function tryDelete(map: Map, key: K): V | undefined { + const current = map.get(key); + if (current !== undefined) { + map.delete(key); + return current; + } + else { + return undefined; + } + } + + /** + * Creates a map from the elements of an array. + * + * @param array the array of input elements. + * @param makeKey a function that produces a key for a given element. + * + * This function makes no effort to avoid collisions; if any two elements produce + * the same key with the given 'makeKey' function, then the element with the higher + * index in the array will be the one associated with the produced key. + */ + export function arrayToMap(array: T[], makeKey: (value: T) => string): Map; + export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue: (value: T) => U): Map; + export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue?: (value: T) => U): Map { + const result = new StringMap(); + for (const value of array) { + result.set(makeKey(value), makeValue ? makeValue(value) : value); + } + return result; + } + + /** + * Adds the value to an array of values associated with the key, and returns the array. + * Creates the array if it does not already exist. + */ + export function multiMapAdd(map: Map, key: K, value: V): V[] { + const values = map.get(key); + if (values) { + values.push(value); + return values; + } + else { + return setAndReturn(map, key, [value]); + } + } + + /** + * Removes a value from an array of values associated with the key. + * Does not preserve the order of those values. + * Does nothing if `key` is not in `map`, or `value` is not in `map[key]`. + */ + export function multiMapRemove(map: Map, key: K, value: V): void { + const values = map.get(key); + if (values) { + unorderedRemoveItem(values, value); + if (!values.length) { + map.delete(key); + } + } + } + + /** True if the maps have the same keys and values. */ + export function mapsAreEqual(left: Map, right: Map, valuesAreEqual?: (left: V, right: V) => boolean): boolean { + if (left === right) return true; + if (!left || !right) return false; + const someInLeftHasNoMatch = someInMap(left, (leftValue, leftKey) => { + if (!right.has(leftKey)) return true; + const rightValue = right.get(leftKey); + return !(valuesAreEqual ? valuesAreEqual(leftValue, rightValue) : leftValue === rightValue); + }); + if (someInLeftHasNoMatch) return false; + const someInRightHasNoMatch = someKeyInMap(right, rightKey => !left.has(rightKey)); + return !someInRightHasNoMatch; + } + + /** + * Creates a sorted array of keys. + * Sorts keys according to the iteration order they would have if they were in an object, instead of from a Map. + * This is so that tests run consistently whether or not we have a Map shim in place. + * The difference between Map iteration order and V8 object insertion order is that V8 moves natrual-number-like keys to the front. + */ + export function sortInV8ObjectInsertionOrder(values: T[], toKey: (t: T) => string): T[] { + const naturalNumberKeys: T[] = []; + const allOtherKeys: T[] = []; + for (const value of values) { + // "0" looks like a natural but "08" doesn't. + const looksLikeNatural = /^(0|([1-9]\d*))$/.test(toKey(value)); + (looksLikeNatural ? naturalNumberKeys : allOtherKeys).push(value); + } + function toInt(value: T): number { + return parseInt(toKey(value), 10); + } + naturalNumberKeys.sort((a, b) => toInt(a) - toInt(b)); + return naturalNumberKeys.concat(allOtherKeys); + } +} + +// Set utilities +/* @internal */ +namespace ts { + /** Union of the `getSet` of each element in the array. */ + export function setAggregate(array: T[], getSet: (t: T) => Set): Set { + const result = new StringSet(); + for (const value of array) { + copySetValuesFromTo(getSet(value), result); + } + return result; + } + + /** Adds all values in `source` to `target`. */ + function copySetValuesFromTo(source: Set, target: Set): void { + source.forEach(value => target.add(value)); + } + + /** Returns the values in `set` satisfying `predicate`. */ + export function filterSetToArray(set: Set, predicate: (value: T) => boolean): T[] { + const result: T[] = []; + set.forEach(value => { + if (predicate(value)) { + result.push(value); + } + }); + return result; + } +} + +// MapLike utilities +/* @internal */ +namespace ts { + const hasOwnProperty = Object.prototype.hasOwnProperty; + + export function clone(object: T): T { + const result: any = {}; + for (const id in object) { + if (hasOwnProperty.call(object, id)) { + result[id] = (object)[id]; + } + } + return result; + } + + /** + * Indicates whether a map-like contains an own property with the specified key. + * + * NOTE: This is intended for use only with MapLike objects. For Map objects, use + * the 'in' operator. + * + * @param map A map-like. + * @param key A property key. + */ + export function hasProperty(map: MapLike, key: string): boolean { + return hasOwnProperty.call(map, key); + } + + /** + * Gets the value of an owned property in a map-like. + * + * NOTE: This is intended for use only with MapLike objects. For Map objects, use + * an indexer. + * + * @param map A map-like. + * @param key A property key. + */ + export function getProperty(map: MapLike, key: string): T | undefined { + return hasOwnProperty.call(map, key) ? map[key] : undefined; + } + + /** + * Gets the owned, enumerable property keys of a map-like. + * + * NOTE: This is intended for use with MapLike objects. For Map objects, use + * Object.keys instead as it offers better performance. + * + * @param map A map-like. + */ + export function getOwnKeys(map: MapLike): string[] { + const keys: string[] = []; + for (const key in map) if (hasOwnProperty.call(map, key)) { + keys.push(key); + } + return keys; + } + + export function assign, T2, T3>(t: T1, arg1: T2, arg2: T3): T1 & T2 & T3; + export function assign, T2>(t: T1, arg1: T2): T1 & T2; + export function assign>(t: T1, ...args: any[]): any; + export function assign>(t: T1, ...args: any[]) { + for (const arg of args) { + for (const p of getOwnKeys(arg)) { + t[p] = arg[p]; + } + } + return t; + } + + /** + * Reduce the properties defined on a map-like (but not from its prototype chain). + * + * @param map The map-like to reduce + * @param callback An aggregation function that is called for each entry in the map + * @param initial The initial value for the reduction. + */ + export function reduceOwnProperties(map: MapLike, callback: (aggregate: U, value: T, key: string) => U, initial: U): U { + let result = initial; + for (const key in map) if (hasOwnProperty.call(map, key)) { + result = callback(result, map[key], String(key)); + } + return result; + } + + /** + * Performs a shallow equality comparison of the contents of two map-likes. + * + * @param left A map-like whose properties should be compared. + * @param right A map-like whose properties should be compared. + */ + export function equalOwnProperties(left: MapLike, right: MapLike, equalityComparer?: (left: T, right: T) => boolean) { + if (left === right) return true; + if (!left || !right) return false; + for (const key in left) if (hasOwnProperty.call(left, key)) { + if (!hasOwnProperty.call(right, key) === undefined) return false; + if (equalityComparer ? !equalityComparer(left[key], right[key]) : left[key] !== right[key]) return false; + } + for (const key in right) if (hasOwnProperty.call(right, key)) { + if (!hasOwnProperty.call(left, key)) return false; + } + return true; + } + + export function extend(first: T1 , second: T2): T1 & T2 { + const result: T1 & T2 = {}; + for (const id in second) if (hasOwnProperty.call(second, id)) { + (result as any)[id] = (second as any)[id]; + } + for (const id in first) if (hasOwnProperty.call(first, id)) { + (result as any)[id] = (first as any)[id]; + } + return result; + } +} diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 4e19aafa4848b..8e55df7fcf3ce 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -59,7 +59,7 @@ namespace ts { let resultHasExternalModuleIndicator: boolean; let currentText: string; let currentLineMap: number[]; - let currentIdentifiers: Map; + let currentIdentifiers: Map; let isCurrentFileExternalModule: boolean; let reportedDeclarationError = false; let errorNameNode: DeclarationName; @@ -75,7 +75,7 @@ namespace ts { // and we could be collecting these paths from multiple files into single one with --out option let referencesOutput = ""; - let usedTypeDirectiveReferences: Map; + let usedTypeDirectiveReferences: Set; // Emit references corresponding to each file const emittedReferencedFiles: SourceFile[] = []; @@ -156,9 +156,9 @@ namespace ts { }); if (usedTypeDirectiveReferences) { - for (const directive in usedTypeDirectiveReferences) { + usedTypeDirectiveReferences.forEach(directive => { referencesOutput += `/// ${newLine}`; - } + }); } return { @@ -267,11 +267,11 @@ namespace ts { } if (!usedTypeDirectiveReferences) { - usedTypeDirectiveReferences = createMap(); + usedTypeDirectiveReferences = new StringSet(); } for (const directive of typeReferenceDirectives) { - if (!(directive in usedTypeDirectiveReferences)) { - usedTypeDirectiveReferences[directive] = directive; + if (!usedTypeDirectiveReferences.has(directive)) { + usedTypeDirectiveReferences.add(directive); } } } @@ -535,14 +535,14 @@ namespace ts { // do not need to keep track of created temp names. function getExportDefaultTempVariableName(): string { const baseName = "_default"; - if (!(baseName in currentIdentifiers)) { + if (!currentIdentifiers.has(baseName)) { return baseName; } let count = 0; while (true) { count++; const name = baseName + "_" + count; - if (!(name in currentIdentifiers)) { + if (!currentIdentifiers.has(name)) { return name; } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index b3242e211de92..4881bfcf3ef76 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -219,11 +219,11 @@ const _super = (function (geti, seti) { let nodeIdToGeneratedName: string[]; let autoGeneratedIdToGeneratedName: string[]; - let generatedNameSet: Map; + let generatedNameSet: Set; let tempFlags: TempFlags; let currentSourceFile: SourceFile; let currentText: string; - let currentFileIdentifiers: Map; + let currentFileIdentifiers: Map; let extendsEmitted: boolean; let assignEmitted: boolean; let decorateEmitted: boolean; @@ -292,7 +292,7 @@ const _super = (function (geti, seti) { sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit); nodeIdToGeneratedName = []; autoGeneratedIdToGeneratedName = []; - generatedNameSet = createMap(); + generatedNameSet = new StringSet(); isOwnFileEmit = !isBundledEmit; // Emit helpers from all the files @@ -2621,15 +2621,16 @@ const _super = (function (geti, seti) { function isUniqueName(name: string): boolean { return !resolver.hasGlobalName(name) && - !hasProperty(currentFileIdentifiers, name) && - !hasProperty(generatedNameSet, name); + !currentFileIdentifiers.has(name) && + !generatedNameSet.has(name); } function isUniqueLocalName(name: string, container: Node): boolean { for (let node = container; isNodeDescendantOf(node, container); node = node.nextContainer) { - if (node.locals && hasProperty(node.locals, name)) { + if (node.locals) { + const local = node.locals.get(name); // We conservatively include alias symbols to cover cases where they're emitted as locals - if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { + if (local && local.flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { return false; } } @@ -2678,7 +2679,8 @@ const _super = (function (geti, seti) { while (true) { const generatedName = baseName + i; if (isUniqueName(generatedName)) { - return generatedNameSet[generatedName] = generatedName; + generatedNameSet.add(generatedName); + return generatedName; } i++; } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index d873c3ef87777..93e830fa1f36a 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2659,9 +2659,9 @@ namespace ts { return destEmitNode; } - function mergeTokenSourceMapRanges(sourceRanges: Map, destRanges: Map) { - if (!destRanges) destRanges = createMap(); - copyProperties(sourceRanges, destRanges); + function mergeTokenSourceMapRanges(sourceRanges: Map, destRanges: Map): Map { + if (!destRanges) destRanges = new NumberMap(); + copyMapEntriesFromTo(sourceRanges, destRanges); return destRanges; } @@ -2753,8 +2753,8 @@ namespace ts { */ export function setTokenSourceMapRange(node: T, token: SyntaxKind, range: TextRange) { const emitNode = getOrCreateEmitNode(node); - const tokenSourceMapRanges = emitNode.tokenSourceMapRanges || (emitNode.tokenSourceMapRanges = createMap()); - tokenSourceMapRanges[token] = range; + const tokenSourceMapRanges = emitNode.tokenSourceMapRanges || (emitNode.tokenSourceMapRanges = new NumberMap()); + tokenSourceMapRanges.set(token, range); return node; } @@ -2795,7 +2795,7 @@ namespace ts { export function getTokenSourceMapRange(node: Node, token: SyntaxKind) { const emitNode = node.emitNode; const tokenSourceMapRanges = emitNode && emitNode.tokenSourceMapRanges; - return tokenSourceMapRanges && tokenSourceMapRanges[token]; + return tokenSourceMapRanges && tokenSourceMapRanges.get(token); } /** @@ -2880,10 +2880,8 @@ namespace ts { * Here we check if alternative name was provided for a given moduleName and return it if possible. */ function tryRenameExternalModule(moduleName: LiteralExpression, sourceFile: SourceFile) { - if (sourceFile.renamedDependencies && hasProperty(sourceFile.renamedDependencies, moduleName.text)) { - return createLiteral(sourceFile.renamedDependencies[moduleName.text]); - } - return undefined; + const rename = sourceFile.renamedDependencies && sourceFile.renamedDependencies.get(moduleName.text); + return rename && createLiteral(rename); } /** diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 51afa9b0a8943..9ff3f78998420 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -490,7 +490,7 @@ namespace ts { let currentToken: SyntaxKind; let sourceText: string; let nodeCount: number; - let identifiers: Map; + let identifiers: Map; let identifierCount: number; let parsingContext: ParsingContext; @@ -600,7 +600,7 @@ namespace ts { parseDiagnostics = []; parsingContext = 0; - identifiers = createMap(); + identifiers = new StringMap(); identifierCount = 0; nodeCount = 0; @@ -1101,7 +1101,7 @@ namespace ts { function internIdentifier(text: string): string { text = escapeIdentifier(text); - return identifiers[text] || (identifiers[text] = text); + return getOrUpdate(identifiers, text, text => text); } // An identifier that starts with two underscores has an extra underscore character prepended to it to avoid issues diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts index e8f064e81cdf3..b94386acc87a6 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -16,9 +16,9 @@ namespace ts.performance { let enabled = false; let profilerStart = 0; - let counts: Map; - let marks: Map; - let measures: Map; + let counts: Map; + let marks: Map; + let measures: Map; /** * Marks a performance event. @@ -27,8 +27,8 @@ namespace ts.performance { */ export function mark(markName: string) { if (enabled) { - marks[markName] = timestamp(); - counts[markName] = (counts[markName] || 0) + 1; + marks.set(markName, timestamp()); + counts.set(markName, (counts.get(markName) || 0) + 1); profilerEvent(markName); } } @@ -44,9 +44,9 @@ namespace ts.performance { */ export function measure(measureName: string, startMarkName?: string, endMarkName?: string) { if (enabled) { - const end = endMarkName && marks[endMarkName] || timestamp(); - const start = startMarkName && marks[startMarkName] || profilerStart; - measures[measureName] = (measures[measureName] || 0) + (end - start); + const end = endMarkName && marks.get(endMarkName) || timestamp(); + const start = startMarkName && marks.get(startMarkName) || profilerStart; + measures.set(measureName, (measures.get(measureName) || 0) + (end - start)); } } @@ -56,7 +56,7 @@ namespace ts.performance { * @param markName The name of the mark. */ export function getCount(markName: string) { - return counts && counts[markName] || 0; + return counts && counts.get(markName) || 0; } /** @@ -65,7 +65,7 @@ namespace ts.performance { * @param measureName The name of the measure whose durations should be accumulated. */ export function getDuration(measureName: string) { - return measures && measures[measureName] || 0; + return measures && measures.get(measureName) || 0; } /** @@ -74,16 +74,14 @@ namespace ts.performance { * @param cb The action to perform for each measure */ export function forEachMeasure(cb: (measureName: string, duration: number) => void) { - for (const key in measures) { - cb(key, measures[key]); - } + measures.forEach((duration, measureName) => cb(measureName, duration)); } /** Enables (and resets) performance measurements for the compiler. */ export function enable() { - counts = createMap(); - marks = createMap(); - measures = createMap(); + counts = new StringMap(); + marks = new StringMap(); + measures = new StringMap(); enabled = true; profilerStart = timestamp(); } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index e300ac22fe59e..cdadbe2a259ff 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -82,7 +82,7 @@ namespace ts { } export function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost { - const existingDirectories = createMap(); + const existingDirectories = new StringSet(); function getCanonicalFileName(fileName: string): string { // if underlying system can distinguish between two files whose names differs only in cases then file name already in canonical form. @@ -114,11 +114,11 @@ namespace ts { } function directoryExists(directoryPath: string): boolean { - if (directoryPath in existingDirectories) { + if (existingDirectories.has(directoryPath)) { return true; } if (sys.directoryExists(directoryPath)) { - existingDirectories[directoryPath] = true; + existingDirectories.add(directoryPath); return true; } return false; @@ -132,21 +132,21 @@ namespace ts { } } - let outputFingerprints: Map; + let outputFingerprints: Map; function writeFileIfUpdated(fileName: string, data: string, writeByteOrderMark: boolean): void { if (!outputFingerprints) { - outputFingerprints = createMap(); + outputFingerprints = new StringMap(); } const hash = sys.createHash(data); const mtimeBefore = sys.getModifiedTime(fileName); - if (mtimeBefore && fileName in outputFingerprints) { - const fingerprint = outputFingerprints[fileName]; + if (mtimeBefore) { + const fingerprint = outputFingerprints.get(fileName); // If output has not been changed, and the file has no external modification - if (fingerprint.byteOrderMark === writeByteOrderMark && + if (fingerprint !== undefined && fingerprint.byteOrderMark === writeByteOrderMark && fingerprint.hash === hash && fingerprint.mtime.getTime() === mtimeBefore.getTime()) { return; @@ -157,11 +157,11 @@ namespace ts { const mtimeAfter = sys.getModifiedTime(fileName); - outputFingerprints[fileName] = { + outputFingerprints.set(fileName, { hash, byteOrderMark: writeByteOrderMark, mtime: mtimeAfter - }; + }); } function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) { @@ -279,11 +279,11 @@ namespace ts { return []; } const resolutions: T[] = []; - const cache = createMap(); + const cache = new StringMap(); for (const name of names) { - const result = name in cache - ? cache[name] - : cache[name] = loader(name, containingFile); + const result = cache.has(name) + ? cache.get(name) + : setAndReturn(cache, name, loader(name, containingFile)); resolutions.push(result); } return resolutions; @@ -295,9 +295,9 @@ namespace ts { let commonSourceDirectory: string; let diagnosticsProducingTypeChecker: TypeChecker; let noDiagnosticsTypeChecker: TypeChecker; - let classifiableNames: Map; + let classifiableNames: Set; - let resolvedTypeReferenceDirectives = createMap(); + let resolvedTypeReferenceDirectives = new StringMap(); let fileProcessingDiagnostics = createDiagnosticCollection(); // The below settings are to track if a .js file should be add to the program if loaded via searching under node_modules. @@ -312,10 +312,10 @@ namespace ts { // If a module has some of its imports skipped due to being at the depth limit under node_modules, then track // this, as it may be imported at a shallower depth later, and then it will need its skipped imports processed. - const modulesWithElidedImports = createMap(); + const modulesWithElidedImports = new StringMap(); // Track source files that are source files found by searching under node_modules, as these shouldn't be compiled. - const sourceFilesFoundSearchingNodeModules = createMap(); + const sourceFilesFoundSearchingNodeModules = new StringMap(); performance.mark("beforeProgram"); @@ -440,15 +440,11 @@ namespace ts { return commonSourceDirectory; } - function getClassifiableNames() { + function getClassifiableNames(): Set { if (!classifiableNames) { // Initialize a checker so that all our files are bound. getTypeChecker(); - classifiableNames = createMap(); - - for (const sourceFile of files) { - copyProperties(sourceFile.classifiableNames, classifiableNames); - } + classifiableNames = setAggregate(files, sourceFile => sourceFile.classifiableNames); } return classifiableNames; @@ -598,7 +594,7 @@ namespace ts { getSourceFile: program.getSourceFile, getSourceFileByPath: program.getSourceFileByPath, getSourceFiles: program.getSourceFiles, - isSourceFileFromExternalLibrary: (file: SourceFile) => !!sourceFilesFoundSearchingNodeModules[file.path], + isSourceFileFromExternalLibrary: (file: SourceFile) => !!sourceFilesFoundSearchingNodeModules.get(file.path), writeFile: writeFileCallback || ( (fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)), isEmitBlocked, @@ -1142,8 +1138,8 @@ namespace ts { // Get source file from normalized fileName function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, isReference: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): SourceFile { - if (filesByName.contains(path)) { - const file = filesByName.get(path); + let file = filesByName.get(path); + if (file !== undefined) { // try to check if we've already seen this file but with a different casing in path // NOTE: this only makes sense for case-insensitive file systems if (file && options.forceConsistentCasingInFileNames && getNormalizedAbsolutePath(file.fileName, currentDirectory) !== getNormalizedAbsolutePath(fileName, currentDirectory)) { @@ -1152,20 +1148,20 @@ namespace ts { // If the file was previously found via a node_modules search, but is now being processed as a root file, // then everything it sucks in may also be marked incorrectly, and needs to be checked again. - if (file && sourceFilesFoundSearchingNodeModules[file.path] && currentNodeModulesDepth == 0) { - sourceFilesFoundSearchingNodeModules[file.path] = false; + if (file && sourceFilesFoundSearchingNodeModules.get(file.path) && currentNodeModulesDepth == 0) { + sourceFilesFoundSearchingNodeModules.set(file.path, false); if (!options.noResolve) { processReferencedFiles(file, getDirectoryPath(fileName), isDefaultLib); processTypeReferenceDirectives(file); } - modulesWithElidedImports[file.path] = false; + modulesWithElidedImports.set(file.path, false); processImportedModules(file, getDirectoryPath(fileName)); } // See if we need to reprocess the imports due to prior skipped imports - else if (file && modulesWithElidedImports[file.path]) { + else if (file && modulesWithElidedImports.get(file.path)) { if (currentNodeModulesDepth < maxNodeModulesJsDepth) { - modulesWithElidedImports[file.path] = false; + modulesWithElidedImports.set(file.path, false); processImportedModules(file, getDirectoryPath(fileName)); } } @@ -1174,7 +1170,7 @@ namespace ts { } // We haven't looked for this file, do so now and cache result - const file = host.getSourceFile(fileName, options.target, hostErrorMessage => { + file = host.getSourceFile(fileName, options.target, hostErrorMessage => { if (refFile !== undefined && refPos !== undefined && refEnd !== undefined) { fileProcessingDiagnostics.add(createFileDiagnostic(refFile, refPos, refEnd - refPos, Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage)); @@ -1186,7 +1182,7 @@ namespace ts { filesByName.set(path, file); if (file) { - sourceFilesFoundSearchingNodeModules[path] = (currentNodeModulesDepth > 0); + sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0); file.path = path; if (host.useCaseSensitiveFileNames()) { @@ -1248,7 +1244,7 @@ namespace ts { refFile?: SourceFile, refPos?: number, refEnd?: number): void { // If we already found this library as a primary reference - nothing to do - const previousResolution = resolvedTypeReferenceDirectives[typeReferenceDirective]; + const previousResolution = resolvedTypeReferenceDirectives.get(typeReferenceDirective); if (previousResolution && previousResolution.primary) { return; } @@ -1285,7 +1281,7 @@ namespace ts { } if (saveResolution) { - resolvedTypeReferenceDirectives[typeReferenceDirective] = resolvedTypeReferenceDirective; + resolvedTypeReferenceDirectives.set(typeReferenceDirective, resolvedTypeReferenceDirective); } } @@ -1305,7 +1301,7 @@ namespace ts { function processImportedModules(file: SourceFile, basePath: string) { collectExternalModuleReferences(file); if (file.imports.length || file.moduleAugmentations.length) { - file.resolvedModules = createMap(); + file.resolvedModules = new StringMap(); const moduleNames = map(concatenate(file.imports, file.moduleAugmentations), getTextOfLiteral); const resolutions = resolveModuleNamesWorker(moduleNames, getNormalizedAbsolutePath(file.fileName, currentDirectory)); for (let i = 0; i < moduleNames.length; i++) { @@ -1329,7 +1325,7 @@ namespace ts { const shouldAddFile = resolution && !options.noResolve && i < file.imports.length && !elideImport; if (elideImport) { - modulesWithElidedImports[file.path] = true; + modulesWithElidedImports.set(file.path, true); } else if (shouldAddFile) { findSourceFile(resolution.resolvedFileName, diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index e7b3872f2e3bb..ccf3fa38950d5 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -56,7 +56,7 @@ namespace ts { tryScan(callback: () => T): T; } - const textToToken = createMap({ + const textToToken = mapOfMapLike({ "abstract": SyntaxKind.AbstractKeyword, "any": SyntaxKind.AnyKeyword, "as": SyntaxKind.AsKeyword, @@ -272,11 +272,11 @@ namespace ts { lookupInUnicodeMap(code, unicodeES3IdentifierPart); } - function makeReverseMap(source: Map): string[] { + function makeReverseMap(source: Map): string[] { const result: string[] = []; - for (const name in source) { - result[source[name]] = name; - } + source.forEach((num, name) => { + result[num] = name; + }); return result; } @@ -288,7 +288,7 @@ namespace ts { /* @internal */ export function stringToToken(s: string): SyntaxKind { - return textToToken[s]; + return textToToken.get(s); } /* @internal */ @@ -362,8 +362,6 @@ namespace ts { return computeLineAndCharacterOfPosition(getLineStarts(sourceFile), position); } - const hasOwnProperty = Object.prototype.hasOwnProperty; - export function isWhiteSpace(ch: number): boolean { return isWhiteSpaceSingleLine(ch) || isLineBreak(ch); } @@ -1182,8 +1180,11 @@ namespace ts { const len = tokenValue.length; if (len >= 2 && len <= 11) { const ch = tokenValue.charCodeAt(0); - if (ch >= CharacterCodes.a && ch <= CharacterCodes.z && hasOwnProperty.call(textToToken, tokenValue)) { - return token = textToToken[tokenValue]; + if (ch >= CharacterCodes.a && ch <= CharacterCodes.z) { + token = textToToken.get(tokenValue); + if (token !== undefined) { + return token; + } } } return token = SyntaxKind.Identifier; diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 46db5188e339c..fcf85aa86a676 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -362,7 +362,7 @@ namespace ts { const emitNode = node && node.emitNode; const emitFlags = emitNode && emitNode.flags; - const range = emitNode && emitNode.tokenSourceMapRanges && emitNode.tokenSourceMapRanges[token]; + const range = emitNode && emitNode.tokenSourceMapRanges && emitNode.tokenSourceMapRanges.get(token); tokenPos = skipTrivia(currentSourceText, range ? range.pos : tokenPos); if ((emitFlags & EmitFlags.NoTokenLeadingSourceMaps) === 0 && tokenPos >= 0) { diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 7cf7d75bc02e2..784c40f4d224d 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -239,25 +239,25 @@ namespace ts { const useNonPollingWatchers = process.env["TSC_NONPOLLING_WATCHER"]; function createWatchedFileSet() { - const dirWatchers = createMap(); + const dirWatchers = new StringMap(); // One file can have multiple watchers - const fileWatcherCallbacks = createMap(); + const fileWatcherCallbacks = new StringMap(); return { addFile, removeFile }; function reduceDirWatcherRefCountForFile(fileName: string) { const dirName = getDirectoryPath(fileName); - const watcher = dirWatchers[dirName]; + const watcher = dirWatchers.get(dirName); if (watcher) { watcher.referenceCount -= 1; if (watcher.referenceCount <= 0) { watcher.close(); - delete dirWatchers[dirName]; + dirWatchers.delete(dirName); } } } function addDirWatcher(dirPath: string): void { - let watcher = dirWatchers[dirPath]; + let watcher = dirWatchers.get(dirPath); if (watcher) { watcher.referenceCount += 1; return; @@ -268,7 +268,7 @@ namespace ts { (eventName: string, relativeFileName: string) => fileEventHandler(eventName, relativeFileName, dirPath) ); watcher.referenceCount = 1; - dirWatchers[dirPath] = watcher; + dirWatchers.set(dirPath, watcher); return; } @@ -298,9 +298,12 @@ namespace ts { ? undefined : ts.getNormalizedAbsolutePath(relativeFileName, baseDirPath); // Some applications save a working file via rename operations - if ((eventName === "change" || eventName === "rename") && fileWatcherCallbacks[fileName]) { - for (const fileCallback of fileWatcherCallbacks[fileName]) { - fileCallback(fileName); + if ((eventName === "change" || eventName === "rename")) { + const callbacks = fileWatcherCallbacks.get(fileName); + if (callbacks) { + for (const fileCallback of fileWatcherCallbacks.get(fileName)) { + fileCallback(fileName); + } } } } diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 92407af2bb9fe..e341fd6f69f04 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -10,14 +10,14 @@ /* @internal */ namespace ts { - const moduleTransformerMap = createMap({ - [ModuleKind.ES6]: transformES6Module, - [ModuleKind.System]: transformSystemModule, - [ModuleKind.AMD]: transformModule, - [ModuleKind.CommonJS]: transformModule, - [ModuleKind.UMD]: transformModule, - [ModuleKind.None]: transformModule, - }); + const moduleTransformerMap = new NumberMap([ + [ModuleKind.ES6, transformES6Module], + [ModuleKind.System, transformSystemModule], + [ModuleKind.AMD, transformModule], + [ModuleKind.CommonJS, transformModule], + [ModuleKind.UMD, transformModule], + [ModuleKind.None, transformModule], + ]); const enum SyntaxKindFeatureFlags { Substitution = 1 << 0, @@ -109,7 +109,7 @@ namespace ts { const transformers: Transformer[] = []; transformers.push(transformTypeScript); - transformers.push(moduleTransformerMap[moduleKind] || moduleTransformerMap[ModuleKind.None]); + transformers.push(moduleTransformerMap.get(moduleKind) || moduleTransformerMap.get(ModuleKind.None)); if (jsx === JsxEmit.React) { transformers.push(transformJsx); @@ -339,4 +339,4 @@ namespace ts { return statements; } } -} \ No newline at end of file +} diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index 7fb0594a6ccba..12ce9d53fd008 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -70,15 +70,15 @@ namespace ts { * set of labels that occurred inside the converted loop * used to determine if labeled jump can be emitted as is or it should be dispatched to calling code */ - labels?: Map; + labels?: Map; /* * collection of labeled jumps that transfer control outside the converted loop. * maps store association 'label -> labelMarker' where * - label - value of label as it appear in code * - label marker - return value that should be interpreted by calling code as 'jump to