diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 49434e80aba..d3217fd70a8 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 51550ef873f..3578aeb5e4b 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]; + 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; + } } - else if (!contains(props, prop)) { - props.push(prop); + else if (containingType.flags & TypeFlags.Union) { + // A union type requires the property to be present in all constituent types + return [undefined, false, 0]; } - 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; } } - } + 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 be4ee2fa07d..69c2a431fc8 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 1aec63bc588..ae88b6d948b 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 e4bc9a29d5b..cdf42ebea4f 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 UnionOrIntersectionType { } + export interface IntersectionType extends TypeOperatorType { } + + /* @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 7a22c5634d8..67ea182bf98 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 b4fd8736b2f..3d050b2cf2c 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 dcda3e40b1a..8766937c008 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;