From 4eedfea81f4603d307829268ea934b7ccf725a3e Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 10 Oct 2016 10:05:31 -0700 Subject: [PATCH] Support unions/intersections in spread types 1. Lift unions and intersections above spread types when creating them in getSpreadType. 2. Cleanup now-unneeded changes to existing union/intersection code. 3. Cleanup some incorrect errors I added earlier. --- src/compiler/checker.ts | 265 +++++++++++++-------------- src/compiler/diagnosticMessages.json | 10 +- src/compiler/types.ts | 12 +- src/services/findAllReferences.ts | 6 +- 4 files changed, 135 insertions(+), 158 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0e7266b223c..9d8ea29a381 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2197,7 +2197,7 @@ 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); @@ -2291,7 +2291,7 @@ namespace ts { } } - function writeUnionOrIntersectionType(type: TypeOperatorType, flags: TypeFormatFlags) { + function writeUnionOrIntersectionType(type: UnionOrIntersectionType, flags: TypeFormatFlags) { if (flags & TypeFormatFlags.InElementType) { writePunctuation(writer, SyntaxKind.OpenParenToken); } @@ -4449,7 +4449,7 @@ namespace ts { } } - function getPropertiesOfUnionOrIntersectionType(type: TypeOperatorType): Symbol[] { + function getPropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType): Symbol[] { for (const current of type.types) { for (const prop of getPropertiesOfType(current)) { getUnionOrIntersectionProperty(type, prop.name); @@ -4499,7 +4499,7 @@ namespace ts { } function getApparentTypeOfSpread(type: SpreadType) { - return getSpreadType([getApparentType(type.left), getApparentType(type.right)], type.symbol, undefined, undefined); + return getSpreadType([getApparentType(type.left), getApparentType(type.right)], type.symbol); } /** @@ -4529,44 +4529,35 @@ namespace ts { return type; } - function createUnionOrIntersectionProperty(containingType: TypeOperatorType, name: string): Symbol { + function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: string) { const types = containingType.types; - 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; - let isPartial = 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; - } + 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; + let isPartial = 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 (containingType.flags & TypeFlags.Union) { - isPartial = true; - } + } + else if (containingType.flags & TypeFlags.Union) { + isPartial = true; } } - return [props, isReadonly, isPartial, commonFlags]; - }); - } - - function createUnionOrIntersectionOrSpreadPropertySymbol(containingType: TypeOperatorType, - name: string, - symbolCreator: () => [Symbol[], boolean, boolean, SymbolFlags]) { - const [props, isReadonly, isPartial, flags] = symbolCreator(); + } if (!props) { return undefined; } @@ -4590,7 +4581,7 @@ namespace ts { } propTypes.push(type); } - const result = createSymbol(SymbolFlags.Property | SymbolFlags.Transient | SymbolFlags.SyntheticProperty | flags, name); + const result = createSymbol(SymbolFlags.Property | SymbolFlags.Transient | SymbolFlags.SyntheticProperty | commonFlags, name); result.syntheticKind === SyntheticSymbolKind.UnionOrIntersection; result.containingType = containingType; result.hasNonUniformType = hasNonUniformType; @@ -4609,7 +4600,7 @@ namespace ts { // constituents, in which case the isPartial flag is set when the containing type is union type. We need // 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: TypeOperatorType, name: string): Symbol { + function getUnionOrIntersectionProperty(type: UnionOrIntersectionType, name: string): Symbol { const properties = type.resolvedProperties || (type.resolvedProperties = createMap()); let property = properties[name]; if (!property) { @@ -4621,7 +4612,7 @@ namespace ts { return property; } - function getPropertyOfUnionOrIntersectionType(type: TypeOperatorType, name: string): Symbol { + function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: string): Symbol { const property = getUnionOrIntersectionProperty(type, name); // We need to filter out partial properties in union types return property && !(property.flags & SymbolFlags.SyntheticProperty && (property).isPartial) ? property : undefined; @@ -4652,7 +4643,7 @@ namespace ts { return getPropertyOfObjectType(globalObjectType, name); } if (type.flags & TypeFlags.UnionOrIntersection) { - return getPropertyOfUnionOrIntersectionType(type, name); + return getPropertyOfUnionOrIntersectionType(type, name); } return undefined; } @@ -5773,6 +5764,9 @@ namespace ts { return spreadTypes[id]; } const right = types.pop(); + if (right.flags & TypeFlags.Any) { + return anyType; + } if (right.flags & TypeFlags.Spread) { // spread is right associative and associativity applies, so transform // (T ... U) ... V to T ... (U ... V) @@ -5783,9 +5777,22 @@ namespace ts { types.push(rspread.right); return getSpreadType(types, symbol, aliasSymbol, aliasTypeArguments); } + if (right.flags & TypeFlags.Intersection) { + const spreads = map((right as IntersectionType).types, + t => getSpreadType(types.slice().concat([t]), symbol, aliasSymbol, aliasTypeArguments)); + return getIntersectionType(spreads, aliasSymbol, aliasTypeArguments); + } + if (right.flags & TypeFlags.Union) { + // TODO: types is mutable, so this block has to happen before the call to `const left = getSpreadType(...)` + // because that call consumes the array. It might be worthwhile to use a simple linked list here instead. + // It would avoid the slice+concat call that is needed here for multiple calls to getSpreadType. + const spreads = map((right as UnionType).types, + t => getSpreadType(types.slice().concat([t]), symbol, aliasSymbol, aliasTypeArguments)); + return getUnionType(spreads, /*subtypeReduction*/ false, aliasSymbol, aliasTypeArguments); + } const atBeginning = types.length === 0; const left = getSpreadType(types, symbol, aliasSymbol, aliasTypeArguments); - if (right.flags & (TypeFlags.Null | TypeFlags.Undefined)) { + if (right.flags & TypeFlags.Primitive || left.flags & TypeFlags.Any) { return left; } if (right.flags & TypeFlags.TypeParameter && @@ -5795,9 +5802,18 @@ namespace ts { // for types like T ... T, just return ... T return left; } - if (!(right.flags & (TypeFlags.Union | TypeFlags.Intersection | TypeFlags.TypeParameter | TypeFlags.Spread)) - && !(left.flags & (TypeFlags.Union | TypeFlags.Intersection | TypeFlags.TypeParameter | TypeFlags.Spread))) { - const members = createMap(); + if (left.flags & TypeFlags.Intersection) { + const spreads = map((left as IntersectionType).types, + t => getSpreadType(types.slice().concat([t, right]), symbol, aliasSymbol, aliasTypeArguments)); + return getIntersectionType(spreads, aliasSymbol, aliasTypeArguments); + } + if (left.flags & TypeFlags.Union) { + const spreads = map((left as UnionType).types, + t => getSpreadType(types.slice().concat([t, right]), symbol, aliasSymbol, aliasTypeArguments)); + return getUnionType(spreads, /*subTypeReduction*/ false, aliasSymbol, aliasTypeArguments); + } + if (right.flags & TypeFlags.ObjectType && left.flags & TypeFlags.ObjectType) { + const members = createMap(); const skippedPrivateMembers = createMap(); let stringIndexInfo = unionIndexInfos(getIndexInfoOfType(left, IndexKind.String), getIndexInfoOfType(right, IndexKind.String)); let numberIndexInfo = unionIndexInfos(getIndexInfoOfType(left, IndexKind.Number), getIndexInfoOfType(right, IndexKind.Number)); @@ -5820,7 +5836,6 @@ namespace ts { for (const leftProp of getPropertiesOfType(left)) { if (leftProp.flags & SymbolFlags.SetAccessor && !(leftProp.flags & SymbolFlags.GetAccessor) || leftProp.name in skippedPrivateMembers) { - // skip set-only properties (methods have already been skipped by the recursive call to getSpreadType) continue; } if (leftProp.name in members) { @@ -5836,7 +5851,7 @@ namespace ts { if (declarations.length) { result.valueDeclaration = declarations[0]; } - result.isReadonly = isReadonlySymbol(rightProp) || isReadonlySymbol(leftProp); + result.isReadonly = isReadonlySymbol(leftProp) || isReadonlySymbol(rightProp); members[leftProp.name] = result; } } @@ -5846,10 +5861,9 @@ namespace ts { } return createAnonymousType(symbol, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); } - // one side is a type parameter (TODO: Or union or intersection) const spread = spreadTypes[id] = createObjectType(TypeFlags.Spread, symbol) as SpreadType; - Debug.assert(!!(left.flags & (TypeFlags.Spread | TypeFlags.ObjectType))); - Debug.assert(!!(right.flags & (TypeFlags.TypeParameter | TypeFlags.ObjectType))); + Debug.assert(!!(left.flags & (TypeFlags.Spread | TypeFlags.ObjectType)), "Left flags: " + left.flags.toString(2)); + Debug.assert(!!(right.flags & (TypeFlags.TypeParameter | TypeFlags.ObjectType)), "Right flags: " + right.flags.toString(2)); spread.left = left as SpreadType | ResolvedType; spread.right = right as TypeParameter | ResolvedType; spread.aliasSymbol = aliasSymbol; @@ -6802,19 +6816,19 @@ namespace ts { } } - if (source.flags & TypeFlags.Spread) { - // you only see this for spreads with type parameters (TODO: and unions/intersections) - if (target.flags & TypeFlags.Spread) { - if (!(spreadTypeRelatedTo(source as SpreadType, target as SpreadType))) { + if (source.flags & TypeFlags.Spread && target.flags & TypeFlags.Spread) { + // you only see this for spreads with type parameters + if (!(spreadTypeRelatedTo(source as SpreadType, target as SpreadType))) { + if (reportErrors) { reportRelationError(headMessage, source, target); - return Ternary.False; - } - const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo; - const apparentSource = getApparentType(source); - if (result = objectTypeRelatedTo(apparentSource, source, getApparentType(target), reportStructuralErrors)) { - errorInfo = saveErrorInfo; - return result; } + return Ternary.False; + } + const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo; + const apparentSource = getApparentType(source); + if (result = objectTypeRelatedTo(apparentSource, source, getApparentType(target), reportStructuralErrors)) { + errorInfo = saveErrorInfo; + return result; } } @@ -6878,40 +6892,27 @@ namespace ts { } function spreadTypeRelatedTo(source: SpreadType, target: SpreadType): boolean { - // (Spread ... Object) | (Spread | Object ... TypeParameter) - // in other words, if the right side is Object, then the left side must be a Spread. - if (source.right.flags & TypeFlags.ObjectType && - target.right.flags & TypeFlags.ObjectType) { + // If the right side of a spread type is ObjectType, then the left side must be a Spread. + // Structural compatibility of the spreads' object types are checked separately in isRelatedTo, + // so just skip them for now. + if (source.right.flags & TypeFlags.ObjectType || target.right.flags & TypeFlags.ObjectType) { + return spreadTypeRelatedTo(source.right.flags & TypeFlags.ObjectType ? source.left as SpreadType : source, + target.right.flags & TypeFlags.ObjectType ? target.left as SpreadType : target); + } + // If both right sides are type parameters, then they must be identical for the spread types to be related. + // It also means that the left sides are either spread types or object types. + + // if one left is object and the other is spread, that means the second has another type parameter. which isn't allowed + if (target.right.symbol !== source.right.symbol) { + return false; + } + if (source.left.flags & TypeFlags.Spread && target.left.flags & TypeFlags.Spread) { + // If the left sides are both spread types, then recursively check them. return spreadTypeRelatedTo(source.left as SpreadType, target.left as SpreadType); } - if (source.right.flags & TypeFlags.ObjectType) { - /// target.right is TypeParameter, skip source.right, but keep looking at target - return spreadTypeRelatedTo(source.left as SpreadType, target); - } - if (target.right.flags & TypeFlags.ObjectType) { - /// source.right is TypeParameter, skip target.right, but keep looking at source - return spreadTypeRelatedTo(source, target.left as SpreadType); - } - else { - // both rights are type parameters, so they must be identical - // and both lefts must be the same: - // if one left is object and the other is spread, that means the second has another type parameter. which isn't allowed - if (target.right.symbol !== source.right.symbol) { - return false; - } - if (source.left.flags & TypeFlags.Spread && target.left.flags & TypeFlags.Spread) { - return spreadTypeRelatedTo(source.left as SpreadType, target.left as SpreadType); - } - else if (source.left.flags & TypeFlags.ObjectType && target.left.flags & TypeFlags.ObjectType) { - return true; // let structural compatibility figure it out later - } - else { - // one side is a spread, so it must have more type parameters, which will not be matched by the other side - // return false immediately instead of descending to find this out. - return false; - } - } - + // If the left sides are both object types, then isRelatedTo will check the structural compatibility next. + // Otherwise, one side has more type parameters than the other and the spread types are not related. + return !!(source.left.flags & TypeFlags.ObjectType && target.left.flags & TypeFlags.ObjectType); } function isIdenticalTo(source: Type, target: Type): Ternary { @@ -6927,8 +6928,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; } } @@ -6951,7 +6952,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; } @@ -6989,7 +6990,7 @@ namespace ts { return false; } - function eachTypeRelatedToSomeType(source: TypeOperatorType, target: TypeOperatorType, reportErrors: boolean): Ternary { + function eachTypeRelatedToSomeType(source: UnionOrIntersectionType, target: UnionOrIntersectionType, reportErrors: boolean): Ternary { let result = Ternary.True; const sourceTypes = source.types; for (const sourceType of sourceTypes) { @@ -7002,7 +7003,7 @@ namespace ts { return result; } - function typeRelatedToSomeType(source: Type, target: TypeOperatorType, reportErrors: boolean): Ternary { + function typeRelatedToSomeType(source: Type, target: UnionOrIntersectionType, reportErrors: boolean): Ternary { const targetTypes = target.types; if (target.flags & TypeFlags.Union && containsType(targetTypes, source)) { return Ternary.True; @@ -7017,7 +7018,7 @@ namespace ts { return Ternary.False; } - function typeRelatedToEachType(source: Type, target: TypeOperatorType, reportErrors: boolean): Ternary { + function typeRelatedToEachType(source: Type, target: UnionOrIntersectionType, reportErrors: boolean): Ternary { let result = Ternary.True; const targetTypes = target.types; for (const targetType of targetTypes) { @@ -7030,7 +7031,7 @@ namespace ts { return result; } - function someTypeRelatedToType(source: TypeOperatorType, target: Type, reportErrors: boolean): Ternary { + function someTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary { const sourceTypes = source.types; if (source.flags & TypeFlags.Union && containsType(sourceTypes, target)) { return Ternary.True; @@ -7045,7 +7046,7 @@ namespace ts { return Ternary.False; } - function eachTypeRelatedToType(source: TypeOperatorType, target: Type, reportErrors: boolean): Ternary { + function eachTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary { let result = Ternary.True; const sourceTypes = source.types; for (const sourceType of sourceTypes) { @@ -7990,10 +7991,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: TypeOperatorType): boolean { + function couldUnionOrIntersectionContainTypeParameters(type: UnionOrIntersectionType): boolean { if (type.couldContainTypeParameters === undefined) { type.couldContainTypeParameters = forEach(type.types, couldContainTypeParameters); } @@ -8001,7 +8002,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) { @@ -8031,7 +8032,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; @@ -8043,14 +8044,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); } } @@ -8059,8 +8060,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) { @@ -8107,7 +8108,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 @@ -8131,7 +8132,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); } @@ -8236,7 +8237,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: TypeOperatorType, typesToRemove: Type[]) { + function removeTypesFromUnionOrIntersection(type: UnionOrIntersectionType, typesToRemove: Type[]) { const reducedTypes: Type[] = []; for (const t of type.types) { if (!typeIdenticalToSomeType(t, typesToRemove)) { @@ -8549,7 +8550,7 @@ namespace ts { return getTypeFacts(constraint || emptyObjectType); } if (flags & TypeFlags.UnionOrIntersection) { - return getTypeFactsOfTypes((type).types); + return getTypeFactsOfTypes((type).types); } return TypeFacts.All; } @@ -10632,13 +10633,6 @@ namespace ts { 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) { @@ -10992,7 +10986,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); @@ -13557,7 +13551,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; @@ -13575,7 +13569,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; @@ -13584,7 +13578,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; @@ -14980,9 +14974,11 @@ namespace ts { forEach(node.members, checkSourceElement); if (produceDiagnostics) { const type = getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node); - checkIndexConstraints(type); - checkTypeForDuplicateIndexSignatures(node); - checkObjectTypeForDuplicateDeclarations(node); + if (type.flags & TypeFlags.ObjectType) { + checkIndexConstraints(type); + checkTypeForDuplicateIndexSignatures(node); + checkObjectTypeForDuplicateDeclarations(node); + } } } @@ -20317,17 +20313,6 @@ namespace ts { for (const prop of node.properties) { if (prop.kind === SyntaxKind.SpreadElementExpression) { - const target = (prop as SpreadElementExpression).expression; - 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; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 37a335f67cf..65d67b6bf83 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1967,17 +1967,9 @@ "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 + "code": 2697 }, "Import declaration '{0}' is using private name '{1}'.": { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 75aaf03f08b..0914f0ba414 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1802,7 +1802,7 @@ namespace ts { export interface JSDocRecordType extends JSDocType { kind: SyntaxKind.JSDocRecordType; - literal: TypeLiteralNode; + literal: TypeLiteralNode; } export interface JSDocTypeReference extends JSDocType { @@ -2536,7 +2536,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?: TypeOperatorType; // Containing union or intersection type for synthetic property + containingType?: UnionOrIntersectionType; // Containing union or intersection type for synthetic property leftSpread?: Symbol; // Left source for synthetic spread property rightSpread?: Symbol; // Right source for synthetic spread property hasNonUniformType?: boolean; // True if constituents have non-uniform types @@ -2755,7 +2755,7 @@ namespace ts { instantiations: Map; // Generic instantiation cache } - export interface TypeOperatorType extends Type { + export interface UnionOrIntersectionType extends Type { types: Type[]; // Constituent types /* @internal */ resolvedProperties: SymbolTable; // Cache of resolved properties @@ -2763,9 +2763,9 @@ namespace ts { couldContainTypeParameters: boolean; } - export interface UnionType extends TypeOperatorType { } + export interface UnionType extends UnionOrIntersectionType { } - export interface IntersectionType extends TypeOperatorType { } + export interface IntersectionType extends UnionOrIntersectionType { } /* @internal */ export interface SpreadType extends Type { @@ -2782,7 +2782,7 @@ namespace ts { /* @internal */ // Resolved object, spread, union, or intersection type - export interface ResolvedType extends ObjectType, TypeOperatorType { + export interface ResolvedType extends ObjectType, UnionOrIntersectionType { members: SymbolTable; // Properties by name properties: Symbol[]; // Properties callSignatures: Signature[]; // Call signatures of type diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 8766937c008..dcda3e40b1a 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: TypeOperatorType, result: Symbol[] = []): Symbol[] { + function getSymbolsForClassAndInterfaceComponents(type: UnionOrIntersectionType, 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;