diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 65545efbcd..708de68ec6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -140,6 +140,7 @@ namespace ts { let tupleTypes: Map = {}; let unionTypes: Map = {}; + let intersectionTypes: Map = {}; let stringLiteralTypes: Map = {}; let emitExtends = false; let emitDecorate = false; @@ -1575,8 +1576,8 @@ namespace ts { else if (type.flags & TypeFlags.Tuple) { writeTupleType(type); } - else if (type.flags & TypeFlags.Union) { - writeUnionType(type, flags); + else if (type.flags & TypeFlags.UnionOrIntersection) { + writeUnionOrIntersectionType(type, flags); } else if (type.flags & TypeFlags.Anonymous) { writeAnonymousType(type, flags); @@ -1595,16 +1596,16 @@ namespace ts { } } - function writeTypeList(types: Type[], union: boolean) { + function writeTypeList(types: Type[], delimiter: SyntaxKind) { for (let i = 0; i < types.length; i++) { if (i > 0) { - if (union) { + if (delimiter !== SyntaxKind.CommaToken) { writeSpace(writer); } - writePunctuation(writer, union ? SyntaxKind.BarToken : SyntaxKind.CommaToken); + writePunctuation(writer, delimiter); writeSpace(writer); } - writeType(types[i], union ? TypeFormatFlags.InElementType : TypeFormatFlags.None); + writeType(types[i], delimiter === SyntaxKind.CommaToken ? TypeFormatFlags.None : TypeFormatFlags.InElementType); } } @@ -1662,15 +1663,15 @@ namespace ts { function writeTupleType(type: TupleType) { writePunctuation(writer, SyntaxKind.OpenBracketToken); - writeTypeList(type.elementTypes, /*union*/ false); + writeTypeList(type.elementTypes, SyntaxKind.CommaToken); writePunctuation(writer, SyntaxKind.CloseBracketToken); } - function writeUnionType(type: UnionType, flags: TypeFormatFlags) { + function writeUnionOrIntersectionType(type: UnionOrIntersectionType, flags: TypeFormatFlags) { if (flags & TypeFormatFlags.InElementType) { writePunctuation(writer, SyntaxKind.OpenParenToken); } - writeTypeList(type.types, /*union*/ true); + writeTypeList(type.types, type.flags & TypeFlags.Union ? SyntaxKind.BarToken : SyntaxKind.AmpersandToken); if (flags & TypeFormatFlags.InElementType) { writePunctuation(writer, SyntaxKind.CloseParenToken); } @@ -1747,7 +1748,7 @@ namespace ts { } function writeLiteralType(type: ObjectType, flags: TypeFormatFlags) { - let resolved = resolveObjectOrUnionTypeMembers(type); + let resolved = resolveStructuredTypeMembers(type); if (!resolved.properties.length && !resolved.stringIndexType && !resolved.numberIndexType) { if (!resolved.callSignatures.length && !resolved.constructSignatures.length) { writePunctuation(writer, SyntaxKind.OpenBraceToken); @@ -2082,6 +2083,7 @@ namespace ts { case SyntaxKind.ArrayType: case SyntaxKind.TupleType: case SyntaxKind.UnionType: + case SyntaxKind.IntersectionType: case SyntaxKind.ParenthesizedType: return isDeclarationVisible(node.parent); @@ -2704,7 +2706,7 @@ namespace ts { if (baseConstructorType.flags & TypeFlags.ObjectType) { // Resolving the members of a class requires us to resolve the base class of that class. // We force resolution here such that we catch circularities now. - resolveObjectOrUnionTypeMembers(baseConstructorType); + resolveStructuredTypeMembers(baseConstructorType); } if (!popTypeResolution()) { error(type.symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_base_expression, symbolToString(type.symbol)); @@ -3034,7 +3036,7 @@ namespace ts { } function resolveTupleTypeMembers(type: TupleType) { - let arrayType = resolveObjectOrUnionTypeMembers(createArrayType(getUnionType(type.elementTypes))); + let arrayType = resolveStructuredTypeMembers(createArrayType(getUnionType(type.elementTypes))); let members = createTupleTypeMemberSymbols(type.elementTypes); addInheritedMembers(members, arrayType.properties); setObjectTypeMembers(type, members, arrayType.callSignatures, arrayType.constructSignatures, arrayType.stringIndexType, arrayType.numberIndexType); @@ -3100,6 +3102,26 @@ namespace ts { setObjectTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexType, numberIndexType); } + function intersectTypes(type1: Type, type2: Type): Type { + return !type1 ? type2 : !type2 ? type1 : getIntersectionType([type1, type2]); + } + + 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). + let callSignatures: Signature[] = emptyArray; + let constructSignatures: Signature[] = emptyArray; + let stringIndexType: Type = undefined; + let numberIndexType: Type = undefined; + for (let t of type.types) { + callSignatures = concatenate(callSignatures, getSignaturesOfType(t, SignatureKind.Call)); + constructSignatures = concatenate(constructSignatures, getSignaturesOfType(t, SignatureKind.Construct)); + stringIndexType = intersectTypes(stringIndexType, getIndexTypeOfType(t, IndexKind.String)); + numberIndexType = intersectTypes(numberIndexType, getIndexTypeOfType(t, IndexKind.Number)); + } + setObjectTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexType, numberIndexType); + } + function resolveAnonymousTypeMembers(type: ObjectType) { let symbol = type.symbol; let members: SymbolTable; @@ -3144,7 +3166,7 @@ namespace ts { setObjectTypeMembers(type, members, callSignatures, constructSignatures, stringIndexType, numberIndexType); } - function resolveObjectOrUnionTypeMembers(type: ObjectType): ResolvedType { + function resolveStructuredTypeMembers(type: ObjectType): ResolvedType { if (!(type).members) { if (type.flags & (TypeFlags.Class | TypeFlags.Interface)) { resolveClassOrInterfaceMembers(type); @@ -3158,6 +3180,9 @@ namespace ts { else if (type.flags & TypeFlags.Union) { resolveUnionTypeMembers(type); } + else if (type.flags & TypeFlags.Intersection) { + resolveIntersectionTypeMembers(type); + } else { resolveTypeReferenceMembers(type); } @@ -3168,16 +3193,16 @@ namespace ts { // Return properties of an object type or an empty array for other types function getPropertiesOfObjectType(type: Type): Symbol[] { if (type.flags & TypeFlags.ObjectType) { - return resolveObjectOrUnionTypeMembers(type).properties; + return resolveStructuredTypeMembers(type).properties; } return emptyArray; } - // If the given type is an object type and that type has a property by the given name, return - // the symbol for that property. Otherwise return undefined. + // If the given type is an object type and that type has a property by the given name, + // return the symbol for that property.Otherwise return undefined. function getPropertyOfObjectType(type: Type, name: string): Symbol { if (type.flags & TypeFlags.ObjectType) { - let resolved = resolveObjectOrUnionTypeMembers(type); + let resolved = resolveStructuredTypeMembers(type); if (hasProperty(resolved.members, name)) { let symbol = resolved.members[name]; if (symbolIsValue(symbol)) { @@ -3187,20 +3212,23 @@ namespace ts { } } - function getPropertiesOfUnionType(type: UnionType): Symbol[] { - let result: Symbol[] = []; - forEach(getPropertiesOfType(type.types[0]), prop => { - let unionProp = getPropertyOfUnionType(type, prop.name); - if (unionProp) { - result.push(unionProp); + function getPropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType): Symbol[] { + for (let current of type.types) { + for (let prop of getPropertiesOfType(current)) { + getPropertyOfUnionOrIntersectionType(type, prop.name); } - }); - return result; + // 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 + if (type.flags & TypeFlags.Union) { + break; + } + } + return type.resolvedProperties ? symbolsToArray(type.resolvedProperties) : emptyArray; } function getPropertiesOfType(type: Type): Symbol[] { type = getApparentType(type); - return type.flags & TypeFlags.Union ? getPropertiesOfUnionType(type) : getPropertiesOfObjectType(type); + return type.flags & TypeFlags.UnionOrIntersection ? getPropertiesOfUnionOrIntersectionType(type) : getPropertiesOfObjectType(type); } /** @@ -3235,24 +3263,33 @@ namespace ts { return type; } - function createUnionProperty(unionType: UnionType, name: string): Symbol { - let types = unionType.types; + function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: string): Symbol { + let types = containingType.types; let props: Symbol[]; for (let current of types) { let type = getApparentType(current); if (type !== unknownType) { let prop = getPropertyOfType(type, name); - if (!prop || getDeclarationFlagsFromSymbol(prop) & (NodeFlags.Private | NodeFlags.Protected)) { + if (prop && !(getDeclarationFlagsFromSymbol(prop) & (NodeFlags.Private | NodeFlags.Protected))) { + if (!props) { + props = [prop]; + } + 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; } - if (!props) { - props = [prop]; - } - else { - props.push(prop); - } } } + if (!props) { + return undefined; + } + if (props.length === 1) { + return props[0]; + } let propTypes: Type[] = []; let declarations: Declaration[] = []; for (let prop of props) { @@ -3261,19 +3298,19 @@ namespace ts { } propTypes.push(getTypeOfSymbol(prop)); } - let result = createSymbol(SymbolFlags.Property | SymbolFlags.Transient | SymbolFlags.UnionProperty, name); - result.unionType = unionType; + let result = createSymbol(SymbolFlags.Property | SymbolFlags.Transient | SymbolFlags.SyntheticProperty, name); + result.containingType = containingType; result.declarations = declarations; - result.type = getUnionType(propTypes); + result.type = containingType.flags & TypeFlags.Union ? getUnionType(propTypes) : getIntersectionType(propTypes); return result; } - function getPropertyOfUnionType(type: UnionType, name: string): Symbol { + function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: string): Symbol { let properties = type.resolvedProperties || (type.resolvedProperties = {}); if (hasProperty(properties, name)) { return properties[name]; } - let property = createUnionProperty(type, name); + let property = createUnionOrIntersectionProperty(type, name); if (property) { properties[name] = property; } @@ -3286,7 +3323,7 @@ namespace ts { function getPropertyOfType(type: Type, name: string): Symbol { type = getApparentType(type); if (type.flags & TypeFlags.ObjectType) { - let resolved = resolveObjectOrUnionTypeMembers(type); + let resolved = resolveStructuredTypeMembers(type); if (hasProperty(resolved.members, name)) { let symbol = resolved.members[name]; if (symbolIsValue(symbol)) { @@ -3301,15 +3338,15 @@ namespace ts { } return getPropertyOfObjectType(globalObjectType, name); } - if (type.flags & TypeFlags.Union) { - return getPropertyOfUnionType(type, name); + if (type.flags & TypeFlags.UnionOrIntersection) { + return getPropertyOfUnionOrIntersectionType(type, name); } return undefined; } - function getSignaturesOfObjectOrUnionType(type: Type, kind: SignatureKind): Signature[] { - if (type.flags & (TypeFlags.ObjectType | TypeFlags.Union)) { - let resolved = resolveObjectOrUnionTypeMembers(type); + function getSignaturesOfStructuredType(type: Type, kind: SignatureKind): Signature[] { + if (type.flags & TypeFlags.StructuredType) { + let resolved = resolveStructuredTypeMembers(type); return kind === SignatureKind.Call ? resolved.callSignatures : resolved.constructSignatures; } return emptyArray; @@ -3318,22 +3355,21 @@ namespace ts { // Return the signatures of the given kind in the given type. Creates synthetic union signatures when necessary and // maps primitive types and type parameters are to their apparent types. function getSignaturesOfType(type: Type, kind: SignatureKind): Signature[] { - return getSignaturesOfObjectOrUnionType(getApparentType(type), kind); + return getSignaturesOfStructuredType(getApparentType(type), kind); } function typeHasCallOrConstructSignatures(type: Type): boolean { let apparentType = getApparentType(type); - if (apparentType.flags & (TypeFlags.ObjectType | TypeFlags.Union)) { - let resolved = resolveObjectOrUnionTypeMembers(type); - return resolved.callSignatures.length > 0 - || resolved.constructSignatures.length > 0; + if (apparentType.flags & TypeFlags.StructuredType) { + let resolved = resolveStructuredTypeMembers(type); + return resolved.callSignatures.length > 0 || resolved.constructSignatures.length > 0; } return false; } - function getIndexTypeOfObjectOrUnionType(type: Type, kind: IndexKind): Type { - if (type.flags & (TypeFlags.ObjectType | TypeFlags.Union)) { - let resolved = resolveObjectOrUnionTypeMembers(type); + function getIndexTypeOfStructuredType(type: Type, kind: IndexKind): Type { + if (type.flags & TypeFlags.StructuredType) { + let resolved = resolveStructuredTypeMembers(type); return kind === IndexKind.String ? resolved.stringIndexType : resolved.numberIndexType; } } @@ -3341,7 +3377,7 @@ namespace ts { // Return the index type of the given kind in the given type. Creates synthetic union index types when necessary and // maps primitive types and type parameters are to their apparent types. function getIndexTypeOfType(type: Type, kind: IndexKind): Type { - return getIndexTypeOfObjectOrUnionType(getApparentType(type), kind); + return getIndexTypeOfStructuredType(getApparentType(type), kind); } // Return list of type parameters with duplicates removed (duplicate identifier errors are generated in the actual @@ -3891,25 +3927,20 @@ namespace ts { return links.resolvedType; } - function addTypeToSortedSet(sortedSet: Type[], type: Type) { - if (type.flags & TypeFlags.Union) { - addTypesToSortedSet(sortedSet, (type).types); + function addTypeToSet(typeSet: Type[], type: Type, typeSetKind: TypeFlags) { + if (type.flags & typeSetKind) { + addTypesToSet(typeSet, (type).types, typeSetKind); } - else { - let i = 0; - let id = type.id; - while (i < sortedSet.length && sortedSet[i].id < id) { - i++; - } - if (i === sortedSet.length || sortedSet[i].id !== id) { - sortedSet.splice(i, 0, type); - } + else if (!contains(typeSet, type)) { + typeSet.push(type); } } - function addTypesToSortedSet(sortedTypes: Type[], types: Type[]) { + // Add the given types to the given type set. Order is preserved, duplicates are removed, + // and nested types of the given kind are flattened into the set. + function addTypesToSet(typeSet: Type[], types: Type[], typeSetKind: TypeFlags) { for (let type of types) { - addTypeToSortedSet(sortedTypes, type); + addTypeToSet(typeSet, type, typeSetKind); } } @@ -3951,6 +3982,10 @@ namespace ts { } } + function compareTypeIds(type1: Type, type2: Type): number { + return type1.id - type2.id; + } + // The noSubtypeReduction flag is there because it isn't possible to always do subtype reduction. The flag // is true when creating a union type from a type node and when instantiating a union type. In both of those // cases subtype reduction has to be deferred to properly support recursive union types. For example, a @@ -3959,26 +3994,27 @@ namespace ts { if (types.length === 0) { return emptyObjectType; } - let sortedTypes: Type[] = []; - addTypesToSortedSet(sortedTypes, types); + let typeSet: Type[] = []; + addTypesToSet(typeSet, types, TypeFlags.Union); + typeSet.sort(compareTypeIds); if (noSubtypeReduction) { - if (containsTypeAny(sortedTypes)) { + if (containsTypeAny(typeSet)) { return anyType; } - removeAllButLast(sortedTypes, undefinedType); - removeAllButLast(sortedTypes, nullType); + removeAllButLast(typeSet, undefinedType); + removeAllButLast(typeSet, nullType); } else { - removeSubtypes(sortedTypes); + removeSubtypes(typeSet); } - if (sortedTypes.length === 1) { - return sortedTypes[0]; + if (typeSet.length === 1) { + return typeSet[0]; } - let id = getTypeListId(sortedTypes); + let id = getTypeListId(typeSet); let type = unionTypes[id]; if (!type) { - type = unionTypes[id] = createObjectType(TypeFlags.Union | getWideningFlagsOfTypes(sortedTypes)); - type.types = sortedTypes; + type = unionTypes[id] = createObjectType(TypeFlags.Union | getWideningFlagsOfTypes(typeSet)); + type.types = typeSet; type.reducedType = noSubtypeReduction ? undefined : type; } return type; @@ -4010,6 +4046,40 @@ namespace ts { return links.resolvedType; } + // We do not perform supertype reduction on intersection types. Intersection types are created only by the & + // type operator and we can't reduce those because we want to support recursive intersection types. For example, + // a type alias of the form "type List = T & { next: List }" cannot be reduced during its declaration. + // Also, unlike union types, the order of the constituent types is preserved in order that overload resolution + // for intersections of types with signatures can be deterministic. + function getIntersectionType(types: Type[]): Type { + if (types.length === 0) { + return emptyObjectType; + } + let typeSet: Type[] = []; + addTypesToSet(typeSet, types, TypeFlags.Intersection); + if (containsTypeAny(typeSet)) { + return anyType; + } + if (typeSet.length === 1) { + return typeSet[0]; + } + let id = getTypeListId(typeSet); + let type = intersectionTypes[id]; + if (!type) { + type = intersectionTypes[id] = createObjectType(TypeFlags.Intersection | getWideningFlagsOfTypes(typeSet)); + type.types = typeSet; + } + return type; + } + + function getTypeFromIntersectionTypeNode(node: IntersectionTypeNode): Type { + let links = getNodeLinks(node); + if (!links.resolvedType) { + links.resolvedType = getIntersectionType(map(node.types, getTypeFromTypeNode)); + } + return links.resolvedType; + } + function getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node: Node): Type { let links = getNodeLinks(node); if (!links.resolvedType) { @@ -4067,6 +4137,8 @@ namespace ts { return getTypeFromTupleTypeNode(node); case SyntaxKind.UnionType: return getTypeFromUnionTypeNode(node); + case SyntaxKind.IntersectionType: + return getTypeFromIntersectionTypeNode(node); case SyntaxKind.ParenthesizedType: return getTypeFromTypeNode((node).type); case SyntaxKind.FunctionType: @@ -4254,6 +4326,9 @@ namespace ts { if (type.flags & TypeFlags.Union) { return getUnionType(instantiateList((type).types, mapper, instantiateType), /*noSubtypeReduction*/ true); } + if (type.flags & TypeFlags.Intersection) { + return getIntersectionType(instantiateList((type).types, mapper, instantiateType)); + } } return type; } @@ -4294,7 +4369,7 @@ namespace ts { function getTypeWithoutSignatures(type: Type): Type { if (type.flags & TypeFlags.ObjectType) { - let resolved = resolveObjectOrUnionTypeMembers(type); + let resolved = resolveStructuredTypeMembers(type); if (resolved.constructSignatures.length) { let result = createObjectType(TypeFlags.Anonymous, type.symbol); result.members = resolved.members; @@ -4414,37 +4489,10 @@ namespace ts { } } let saveErrorInfo = errorInfo; - if (source.flags & TypeFlags.Union || target.flags & TypeFlags.Union) { - if (relation === identityRelation) { - if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union) { - if (result = unionTypeRelatedToUnionType(source, target)) { - if (result &= unionTypeRelatedToUnionType(target, source)) { - return result; - } - } - } - else if (source.flags & TypeFlags.Union) { - if (result = unionTypeRelatedToType(source, target, reportErrors)) { - return result; - } - } - else { - if (result = unionTypeRelatedToType(target, source, reportErrors)) { - return result; - } - } - } - else { - if (source.flags & TypeFlags.Union) { - if (result = unionTypeRelatedToType(source, target, reportErrors)) { - return result; - } - } - else { - if (result = typeRelatedToUnionType(source, target, reportErrors)) { - return result; - } - } + if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (source).target === (target).target) { + // We have type references to same target type, see if relationship holds for all type arguments + if (result = typesRelatedTo((source).typeArguments, (target).typeArguments, reportErrors)) { + return result; } } else if (source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.TypeParameter) { @@ -4452,10 +4500,43 @@ namespace ts { return result; } } - else if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (source).target === (target).target) { - // We have type references to same target type, see if relationship holds for all type arguments - if (result = typesRelatedTo((source).typeArguments, (target).typeArguments, reportErrors)) { - return result; + else if (relation !== identityRelation) { + // Note that the "each" checks must precede the "some" checks to produce the correct results + if (source.flags & TypeFlags.Union) { + if (result = eachTypeRelatedToType(source, target, reportErrors)) { + return result; + } + } + else if (target.flags & TypeFlags.Intersection) { + if (result = typeRelatedToEachType(source, target, reportErrors)) { + return result; + } + } + else { + // It is necessary to try "each" checks on both sides because there may be nested "some" checks + // on either side that need to be prioritized. For example, A | B = (A | B) & (C | D) or + // A & B = (A & B) | (C & D). + if (source.flags & TypeFlags.Intersection) { + // If target is a union type the following check will report errors so we suppress them here + if (result = someTypeRelatedToType(source, target, reportErrors && !(target.flags & TypeFlags.Union))) { + return result; + } + } + if (target.flags & TypeFlags.Union) { + if (result = typeRelatedToSomeType(source, target, reportErrors)) { + return result; + } + } + } + } + else { + if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union || + source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) { + if (result = eachTypeRelatedToSomeType(source, target)) { + if (result &= eachTypeRelatedToSomeType(target, source)) { + return result; + } + } } } @@ -4463,17 +4544,20 @@ namespace ts { // it may hold in a structural comparison. // Report structural errors only if we haven't reported any errors yet let reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo; - // identity relation does not use apparent type + // Identity relation does not use apparent type let sourceOrApparentType = relation === identityRelation ? source : getApparentType(source); - if (sourceOrApparentType.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType) { + // 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 (sourceOrApparentType.flags & (TypeFlags.ObjectType | TypeFlags.Intersection) && target.flags & TypeFlags.ObjectType) { if (result = objectTypeRelatedTo(sourceOrApparentType, target, reportStructuralErrors)) { errorInfo = saveErrorInfo; return result; } } - else if (source.flags & TypeFlags.TypeParameter && sourceOrApparentType.flags & TypeFlags.Union) { + else if (source.flags & TypeFlags.TypeParameter && sourceOrApparentType.flags & TypeFlags.UnionOrIntersection) { // We clear the errors first because the following check often gives a better error than - // the union comparison above if it is applicable. + // the union or intersection comparison above if it is applicable. errorInfo = saveErrorInfo; if (result = isRelatedTo(sourceOrApparentType, target, reportErrors)) { return result; @@ -4493,11 +4577,11 @@ namespace ts { return Ternary.False; } - function unionTypeRelatedToUnionType(source: UnionType, target: UnionType): Ternary { + function eachTypeRelatedToSomeType(source: UnionOrIntersectionType, target: UnionOrIntersectionType): Ternary { let result = Ternary.True; let sourceTypes = source.types; for (let sourceType of sourceTypes) { - let related = typeRelatedToUnionType(sourceType, target, false); + let related = typeRelatedToSomeType(sourceType, target, false); if (!related) { return Ternary.False; } @@ -4506,7 +4590,7 @@ namespace ts { return result; } - function typeRelatedToUnionType(source: Type, target: UnionType, reportErrors: boolean): Ternary { + function typeRelatedToSomeType(source: Type, target: UnionOrIntersectionType, reportErrors: boolean): Ternary { let targetTypes = target.types; for (let i = 0, len = targetTypes.length; i < len; i++) { let related = isRelatedTo(source, targetTypes[i], reportErrors && i === len - 1); @@ -4517,7 +4601,31 @@ namespace ts { return Ternary.False; } - function unionTypeRelatedToType(source: UnionType, target: Type, reportErrors: boolean): Ternary { + function typeRelatedToEachType(source: Type, target: UnionOrIntersectionType, reportErrors: boolean): Ternary { + let result = Ternary.True; + let targetTypes = target.types; + for (let targetType of targetTypes) { + let related = isRelatedTo(source, targetType, reportErrors); + if (!related) { + return Ternary.False; + } + result &= related; + } + return result; + } + + function someTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary { + let sourceTypes = source.types; + for (let i = 0, len = sourceTypes.length; i < len; i++) { + let related = isRelatedTo(sourceTypes[i], target, reportErrors && i === len - 1); + if (related) { + return related; + } + } + return Ternary.False; + } + + function eachTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary { let result = Ternary.True; let sourceTypes = source.types; for (let sourceType of sourceTypes) { @@ -4572,7 +4680,7 @@ namespace ts { // Third, check if both types are part of deeply nested chains of generic type instantiations and if so assume the types are // equal and infinitely expanding. Fourth, if we have reached a depth of 100 nested comparisons, assume we have runaway recursion // and issue an error. Otherwise, actually compare the structure of the two types. - function objectTypeRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): Ternary { + function objectTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary { if (overflow) { return Ternary.False; } @@ -4647,7 +4755,7 @@ namespace ts { return result; } - function propertiesRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): Ternary { + function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary { if (relation === identityRelation) { return propertiesIdenticalTo(source, target); } @@ -4731,7 +4839,10 @@ namespace ts { return result; } - function propertiesIdenticalTo(source: ObjectType, target: ObjectType): Ternary { + function propertiesIdenticalTo(source: Type, target: Type): Ternary { + if (!(source.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType)) { + return Ternary.False; + } let sourceProperties = getPropertiesOfObjectType(source); let targetProperties = getPropertiesOfObjectType(target); if (sourceProperties.length !== targetProperties.length) { @@ -4752,7 +4863,7 @@ namespace ts { return result; } - function signaturesRelatedTo(source: ObjectType, target: ObjectType, kind: SignatureKind, reportErrors: boolean): Ternary { + function signaturesRelatedTo(source: Type, target: Type, kind: SignatureKind, reportErrors: boolean): Ternary { if (relation === identityRelation) { return signaturesIdenticalTo(source, target, kind); } @@ -4878,7 +4989,7 @@ namespace ts { return result & isRelatedTo(s, t, reportErrors); } - function signaturesIdenticalTo(source: ObjectType, target: ObjectType, kind: SignatureKind): Ternary { + function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary { let sourceSignatures = getSignaturesOfType(source, kind); let targetSignatures = getSignaturesOfType(target, kind); if (sourceSignatures.length !== targetSignatures.length) { @@ -4895,7 +5006,7 @@ namespace ts { return result; } - function stringIndexTypesRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): Ternary { + function stringIndexTypesRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary { if (relation === identityRelation) { return indexTypesIdenticalTo(IndexKind.String, source, target); } @@ -4920,7 +5031,7 @@ namespace ts { return Ternary.True; } - function numberIndexTypesRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): Ternary { + function numberIndexTypesRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary { if (relation === identityRelation) { return indexTypesIdenticalTo(IndexKind.Number, source, target); } @@ -4953,7 +5064,7 @@ namespace ts { return Ternary.True; } - function indexTypesIdenticalTo(indexKind: IndexKind, source: ObjectType, target: ObjectType): Ternary { + function indexTypesIdenticalTo(indexKind: IndexKind, source: Type, target: Type): Ternary { let targetType = getIndexTypeOfType(target, indexKind); let sourceType = getIndexTypeOfType(source, indexKind); if (!sourceType && !targetType) { @@ -5337,11 +5448,11 @@ namespace ts { inferFromTypes(sourceTypes[i], targetTypes[i]); } } - else if (target.flags & TypeFlags.Union) { - let targetTypes = (target).types; + else if (target.flags & TypeFlags.UnionOrIntersection) { + let targetTypes = (target).types; let typeParameterCount = 0; let typeParameter: TypeParameter; - // First infer to each type in union that isn't a type parameter + // First infer to each type in union or intersection that isn't a type parameter for (let t of targetTypes) { if (t.flags & TypeFlags.TypeParameter && contains(context.typeParameters, t)) { typeParameter = t; @@ -5351,16 +5462,19 @@ namespace ts { inferFromTypes(source, t); } } - // If union contains a single naked type parameter, make a secondary inference to that type parameter - if (typeParameterCount === 1) { + // Next, if target is a union type containing a single naked type parameter, make a + // secondary inference to that type parameter. We don't do this for intersection types + // because in a target type like Foo & T we don't know how which parts of the source type + // should be matched by Foo and which should be inferred to T. + if (target.flags & TypeFlags.Union && typeParameterCount === 1) { inferiority++; inferFromTypes(source, typeParameter); inferiority--; } } - else if (source.flags & TypeFlags.Union) { - // Source is a union type, infer from each consituent type - let sourceTypes = (source).types; + else if (source.flags & TypeFlags.UnionOrIntersection) { + // Source is a union or intersection type, infer from each consituent type + let sourceTypes = (source).types; for (let sourceType of sourceTypes) { inferFromTypes(sourceType, target); } @@ -6343,13 +6457,13 @@ namespace ts { function getTypeOfPropertyOfContextualType(type: Type, name: string) { return applyToContextualType(type, t => { - let prop = getPropertyOfObjectType(t, name); + let prop = t.flags & TypeFlags.StructuredType ? getPropertyOfType(t, name) : undefined; return prop ? getTypeOfSymbol(prop) : undefined; }); } function getIndexTypeOfContextualType(type: Type, kind: IndexKind) { - return applyToContextualType(type, t => getIndexTypeOfObjectOrUnionType(t, kind)); + return applyToContextualType(type, t => getIndexTypeOfStructuredType(t, kind)); } // Return true if the given contextual type is a tuple-like type @@ -6359,7 +6473,7 @@ namespace ts { // Return true if the given contextual type provides an index signature of the given kind function contextualTypeHasIndexSignature(type: Type, kind: IndexKind): boolean { - return !!(type.flags & TypeFlags.Union ? forEach((type).types, t => getIndexTypeOfObjectOrUnionType(t, kind)) : getIndexTypeOfObjectOrUnionType(type, kind)); + return !!(type.flags & TypeFlags.Union ? forEach((type).types, t => getIndexTypeOfStructuredType(t, kind)) : getIndexTypeOfStructuredType(type, kind)); } // In an object literal contextually typed by a type T, the contextual type of a property assignment is the type of @@ -6496,7 +6610,7 @@ namespace ts { // If the given type is an object or union type, if that type has a single signature, and if // that signature is non-generic, return the signature. Otherwise return undefined. function getNonGenericSignature(type: Type): Signature { - let signatures = getSignaturesOfObjectOrUnionType(type, SignatureKind.Call); + let signatures = getSignaturesOfStructuredType(type, SignatureKind.Call); if (signatures.length === 1) { let signature = signatures[0]; if (!signature.typeParameters) { @@ -6538,7 +6652,7 @@ namespace ts { // The signature set of all constituent type with call signatures should match // So number of signatures allowed is either 0 or 1 if (signatureList && - getSignaturesOfObjectOrUnionType(current, SignatureKind.Call).length > 1) { + getSignaturesOfStructuredType(current, SignatureKind.Call).length > 1) { return undefined; } @@ -7691,7 +7805,7 @@ namespace ts { // If type has a single call signature and no other members, return that signature. Otherwise, return undefined. function getSingleCallSignature(type: Type): Signature { if (type.flags & TypeFlags.ObjectType) { - let resolved = resolveObjectOrUnionTypeMembers(type); + let resolved = resolveStructuredTypeMembers(type); if (resolved.callSignatures.length === 1 && resolved.constructSignatures.length === 0 && resolved.properties.length === 0 && !resolved.stringIndexType && !resolved.numberIndexType) { return resolved.callSignatures[0]; @@ -9131,8 +9245,8 @@ namespace ts { if (type.flags & kind) { return true; } - if (type.flags & TypeFlags.Union) { - let types = (type).types; + if (type.flags & TypeFlags.UnionOrIntersection) { + let types = (type).types; for (let current of types) { if (current.flags & kind) { return true; @@ -9143,13 +9257,13 @@ namespace ts { return false; } - // Return true if type has the given flags, or is a union type composed of types that all have those flags. + // Return true if type has the given flags, or is a union or intersection type composed of types that all have those flags. function allConstituentTypesHaveKind(type: Type, kind: TypeFlags): boolean { if (type.flags & kind) { return true; } - if (type.flags & TypeFlags.Union) { - let types = (type).types; + if (type.flags & TypeFlags.UnionOrIntersection) { + let types = (type).types; for (let current of types) { if (!(current.flags & kind)) { return false; @@ -10174,7 +10288,7 @@ namespace ts { forEach(node.elementTypes, checkSourceElement); } - function checkUnionType(node: UnionTypeNode) { + function checkUnionOrIntersectionType(node: UnionOrIntersectionTypeNode) { forEach(node.types, checkSourceElement); } @@ -12951,7 +13065,8 @@ namespace ts { case SyntaxKind.TupleType: return checkTupleType(node); case SyntaxKind.UnionType: - return checkUnionType(node); + case SyntaxKind.IntersectionType: + return checkUnionOrIntersectionType(node); case SyntaxKind.ParenthesizedType: return checkSourceElement((node).type); case SyntaxKind.FunctionDeclaration: @@ -13593,10 +13708,10 @@ namespace ts { } function getRootSymbols(symbol: Symbol): Symbol[] { - if (symbol.flags & SymbolFlags.UnionProperty) { + if (symbol.flags & SymbolFlags.SyntheticProperty) { let symbols: Symbol[] = []; let name = symbol.name; - forEach(getSymbolLinks(symbol).unionType.types, t => { + forEach(getSymbolLinks(symbol).containingType.types, t => { symbols.push(getPropertyOfType(t, name)); }); return symbols; @@ -13893,6 +14008,7 @@ namespace ts { case SyntaxKind.TypeQuery: case SyntaxKind.TypeLiteral: case SyntaxKind.UnionType: + case SyntaxKind.IntersectionType: case SyntaxKind.AnyKeyword: break; default: diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index af009ce36c..8c012f2678 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -340,6 +340,8 @@ namespace ts { return emitTupleType(type); case SyntaxKind.UnionType: return emitUnionType(type); + case SyntaxKind.IntersectionType: + return emitIntersectionType(type); case SyntaxKind.ParenthesizedType: return emitParenType(type); case SyntaxKind.FunctionType: @@ -416,6 +418,10 @@ namespace ts { emitSeparatedList(type.types, " | ", emitType); } + function emitIntersectionType(type: IntersectionTypeNode) { + emitSeparatedList(type.types, " & ", emitType); + } + function emitParenType(type: ParenthesizedTypeNode) { write("("); emitType(type.type); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index f2f39b7495..2d3746212b 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -115,7 +115,8 @@ namespace ts { case SyntaxKind.TupleType: return visitNodes(cbNodes, (node).elementTypes); case SyntaxKind.UnionType: - return visitNodes(cbNodes, (node).types); + case SyntaxKind.IntersectionType: + return visitNodes(cbNodes, (node).types); case SyntaxKind.ParenthesizedType: return visitNode(cbNode, (node).type); case SyntaxKind.ObjectBindingPattern: @@ -2401,22 +2402,30 @@ namespace ts { return type; } - function parseUnionTypeOrHigher(): TypeNode { - let type = parseArrayTypeOrHigher(); - if (token === SyntaxKind.BarToken) { + function parseUnionOrIntersectionType(kind: SyntaxKind, parseConstituentType: () => TypeNode, operator: SyntaxKind): TypeNode { + let type = parseConstituentType(); + if (token === operator) { let types = >[type]; types.pos = type.pos; - while (parseOptional(SyntaxKind.BarToken)) { - types.push(parseArrayTypeOrHigher()); + while (parseOptional(operator)) { + types.push(parseConstituentType()); } types.end = getNodeEnd(); - let node = createNode(SyntaxKind.UnionType, type.pos); + let node = createNode(kind, type.pos); node.types = types; type = finishNode(node); } return type; } + function parseIntersectionTypeOrHigher(): TypeNode { + return parseUnionOrIntersectionType(SyntaxKind.IntersectionType, parseArrayTypeOrHigher, SyntaxKind.AmpersandToken); + } + + function parseUnionTypeOrHigher(): TypeNode { + return parseUnionOrIntersectionType(SyntaxKind.UnionType, parseIntersectionTypeOrHigher, SyntaxKind.BarToken); + } + function isStartOfFunctionType(): boolean { if (token === SyntaxKind.LessThanToken) { return true; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4617ad98b0..4117ad09f7 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -191,6 +191,7 @@ namespace ts { ArrayType, TupleType, UnionType, + IntersectionType, ParenthesizedType, // Binding patterns ObjectBindingPattern, @@ -667,10 +668,14 @@ namespace ts { elementTypes: NodeArray; } - export interface UnionTypeNode extends TypeNode { + export interface UnionOrIntersectionTypeNode extends TypeNode { types: NodeArray; } + export interface UnionTypeNode extends UnionOrIntersectionTypeNode { } + + export interface IntersectionTypeNode extends UnionOrIntersectionTypeNode { } + export interface ParenthesizedTypeNode extends TypeNode { type: TypeNode; } @@ -1573,7 +1578,7 @@ namespace ts { Merged = 0x02000000, // Merged symbol (created during program binding) Transient = 0x04000000, // Transient symbol (created during type check) Prototype = 0x08000000, // Prototype property (no source representation) - UnionProperty = 0x10000000, // Property in union type + SyntheticProperty = 0x10000000, // Property in union or intersection type Optional = 0x20000000, // Optional property ExportStar = 0x40000000, // Export * declaration @@ -1652,7 +1657,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 - unionType?: UnionType; // Containing union type for union property + containingType?: UnionOrIntersectionType; // Containing union or intersection type for synthetic property resolvedExports?: SymbolTable; // Resolved exports of module exportsChecked?: boolean; // True if exports of external module have been checked isNestedRedeclaration?: boolean; // True if symbol is block scoped redeclaration @@ -1721,17 +1726,18 @@ namespace ts { Interface = 0x00000800, // Interface Reference = 0x00001000, // Generic type reference Tuple = 0x00002000, // Tuple - Union = 0x00004000, // Union - Anonymous = 0x00008000, // Anonymous - Instantiated = 0x00010000, // Instantiated anonymous type + Union = 0x00004000, // Union (T | U) + Intersection = 0x00008000, // Intersection (T & U) + Anonymous = 0x00010000, // Anonymous + Instantiated = 0x00020000, // Instantiated anonymous type /* @internal */ - FromSignature = 0x00020000, // Created for signature assignment check - ObjectLiteral = 0x00040000, // Originates in an object literal + FromSignature = 0x00040000, // Created for signature assignment check + ObjectLiteral = 0x00080000, // Originates in an object literal /* @internal */ - ContainsUndefinedOrNull = 0x00080000, // Type is or contains Undefined or Null type + ContainsUndefinedOrNull = 0x00100000, // Type is or contains Undefined or Null type /* @internal */ - ContainsObjectLiteral = 0x00100000, // Type is or contains object literal type - ESSymbol = 0x00200000, // Type of symbol primitive introduced in ES6 + ContainsObjectLiteral = 0x00200000, // Type is or contains object literal type + ESSymbol = 0x00400000, // Type of symbol primitive introduced in ES6 /* @internal */ Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null, @@ -1740,6 +1746,8 @@ namespace ts { StringLike = String | StringLiteral, NumberLike = Number | Enum, ObjectType = Class | Interface | Reference | Tuple | Anonymous, + UnionOrIntersection = Union | Intersection, + StructuredType = ObjectType | Union | Intersection, /* @internal */ RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral } @@ -1799,7 +1807,7 @@ namespace ts { baseArrayType: TypeReference; // Array where T is best common type of element types } - export interface UnionType extends Type { + export interface UnionOrIntersectionType extends Type { types: Type[]; // Constituent types /* @internal */ reducedType: Type; // Reduced union type (all subtypes removed) @@ -1807,9 +1815,13 @@ namespace ts { resolvedProperties: SymbolTable; // Cache of resolved properties } + export interface UnionType extends UnionOrIntersectionType { } + + export interface IntersectionType extends UnionOrIntersectionType { } + /* @internal */ - // Resolved object or union type - export interface ResolvedType extends ObjectType, UnionType { + // Resolved object, union, or intersection type + 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/services.ts b/src/services/services.ts index 3efecd6857..e401182fb1 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3689,7 +3689,7 @@ namespace ts { if (flags & SymbolFlags.Constructor) return ScriptElementKind.constructorImplementationElement; if (flags & SymbolFlags.Property) { - if (flags & SymbolFlags.UnionProperty) { + if (flags & SymbolFlags.SyntheticProperty) { // If union property is result of union of non method (property/accessors/variables), it is labeled as property let unionPropertyKind = forEach(typeChecker.getRootSymbols(symbol), rootSymbol => { let rootSymbolFlags = rootSymbol.getFlags(); @@ -5159,7 +5159,7 @@ namespace ts { // if this symbol is visible from its parent container, e.g. exported, then bail out // if symbol correspond to the union property - bail out - if (symbol.parent || (symbol.flags & SymbolFlags.UnionProperty)) { + if (symbol.parent || (symbol.flags & SymbolFlags.SyntheticProperty)) { return undefined; } diff --git a/tests/baselines/reference/APISample_linter.js b/tests/baselines/reference/APISample_linter.js index b49c2d7596..30951b90fc 100644 --- a/tests/baselines/reference/APISample_linter.js +++ b/tests/baselines/reference/APISample_linter.js @@ -75,26 +75,26 @@ function delint(sourceFile) { delintNode(sourceFile); function delintNode(node) { switch (node.kind) { - case 195 /* ForStatement */: - case 196 /* ForInStatement */: - case 194 /* WhileStatement */: - case 193 /* DoStatement */: - if (node.statement.kind !== 188 /* Block */) { + case 196 /* ForStatement */: + case 197 /* ForInStatement */: + case 195 /* WhileStatement */: + case 194 /* DoStatement */: + if (node.statement.kind !== 189 /* Block */) { report(node, "A looping statement's contents should be wrapped in a block body."); } break; - case 192 /* IfStatement */: + case 193 /* IfStatement */: var ifStatement = node; - if (ifStatement.thenStatement.kind !== 188 /* Block */) { + if (ifStatement.thenStatement.kind !== 189 /* Block */) { report(ifStatement.thenStatement, "An if statement's contents should be wrapped in a block body."); } if (ifStatement.elseStatement && - ifStatement.elseStatement.kind !== 188 /* Block */ && - ifStatement.elseStatement.kind !== 192 /* IfStatement */) { + ifStatement.elseStatement.kind !== 189 /* Block */ && + ifStatement.elseStatement.kind !== 193 /* IfStatement */) { report(ifStatement.elseStatement, "An else statement's contents should be wrapped in a block body."); } break; - case 177 /* BinaryExpression */: + case 178 /* BinaryExpression */: var op = node.operatorToken.kind; if (op === 29 /* EqualsEqualsToken */ || op == 30 /* ExclamationEqualsToken */) { report(node, "Use '===' and '!=='."); diff --git a/tests/baselines/reference/contextualIntersectionType.js b/tests/baselines/reference/contextualIntersectionType.js new file mode 100644 index 0000000000..d61fc52fce --- /dev/null +++ b/tests/baselines/reference/contextualIntersectionType.js @@ -0,0 +1,14 @@ +//// [contextualIntersectionType.ts] +var x: { a: (s: string) => string } & { b: (n: number) => number }; +x = { + a: s => s, + b: n => n +}; + + +//// [contextualIntersectionType.js] +var x; +x = { + a: function (s) { return s; }, + b: function (n) { return n; } +}; diff --git a/tests/baselines/reference/contextualIntersectionType.symbols b/tests/baselines/reference/contextualIntersectionType.symbols new file mode 100644 index 0000000000..cb7ac9f3d4 --- /dev/null +++ b/tests/baselines/reference/contextualIntersectionType.symbols @@ -0,0 +1,23 @@ +=== tests/cases/conformance/types/intersection/contextualIntersectionType.ts === +var x: { a: (s: string) => string } & { b: (n: number) => number }; +>x : Symbol(x, Decl(contextualIntersectionType.ts, 0, 3)) +>a : Symbol(a, Decl(contextualIntersectionType.ts, 0, 8)) +>s : Symbol(s, Decl(contextualIntersectionType.ts, 0, 13)) +>b : Symbol(b, Decl(contextualIntersectionType.ts, 0, 39)) +>n : Symbol(n, Decl(contextualIntersectionType.ts, 0, 44)) + +x = { +>x : Symbol(x, Decl(contextualIntersectionType.ts, 0, 3)) + + a: s => s, +>a : Symbol(a, Decl(contextualIntersectionType.ts, 1, 5)) +>s : Symbol(s, Decl(contextualIntersectionType.ts, 2, 6)) +>s : Symbol(s, Decl(contextualIntersectionType.ts, 2, 6)) + + b: n => n +>b : Symbol(b, Decl(contextualIntersectionType.ts, 2, 14)) +>n : Symbol(n, Decl(contextualIntersectionType.ts, 3, 6)) +>n : Symbol(n, Decl(contextualIntersectionType.ts, 3, 6)) + +}; + diff --git a/tests/baselines/reference/contextualIntersectionType.types b/tests/baselines/reference/contextualIntersectionType.types new file mode 100644 index 0000000000..4967979d73 --- /dev/null +++ b/tests/baselines/reference/contextualIntersectionType.types @@ -0,0 +1,27 @@ +=== tests/cases/conformance/types/intersection/contextualIntersectionType.ts === +var x: { a: (s: string) => string } & { b: (n: number) => number }; +>x : { a: (s: string) => string; } & { b: (n: number) => number; } +>a : (s: string) => string +>s : string +>b : (n: number) => number +>n : number + +x = { +>x = { a: s => s, b: n => n} : { a: (s: string) => string; b: (n: number) => number; } +>x : { a: (s: string) => string; } & { b: (n: number) => number; } +>{ a: s => s, b: n => n} : { a: (s: string) => string; b: (n: number) => number; } + + a: s => s, +>a : (s: string) => string +>s => s : (s: string) => string +>s : string +>s : string + + b: n => n +>b : (n: number) => number +>n => n : (n: number) => number +>n : number +>n : number + +}; + diff --git a/tests/baselines/reference/intersectionAndUnionTypes.errors.txt b/tests/baselines/reference/intersectionAndUnionTypes.errors.txt new file mode 100644 index 0000000000..4f26cc63ba --- /dev/null +++ b/tests/baselines/reference/intersectionAndUnionTypes.errors.txt @@ -0,0 +1,162 @@ +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(19,1): error TS2322: Type 'A' is not assignable to type 'A & B'. + Type 'A' is not assignable to type 'B'. +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(20,1): error TS2322: Type 'B' is not assignable to type 'A & B'. + Type 'B' is not assignable to type 'A'. + Property 'a' is missing in type 'B'. +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(23,1): error TS2322: Type 'A | B' is not assignable to type '(A & B) | (C & D)'. + Type 'A' is not assignable to type '(A & B) | (C & D)'. + Type 'A' is not assignable to type 'C & D'. + Type 'A' is not assignable to type 'C'. +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(25,1): error TS2322: Type 'C | D' is not assignable to type '(A & B) | (C & D)'. + Type 'C' is not assignable to type '(A & B) | (C & D)'. + Type 'C' is not assignable to type 'C & D'. + Type 'C' is not assignable to type 'D'. +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(26,1): error TS2322: Type '(A & B) | (C & D)' is not assignable to type 'A & B'. + Type 'C & D' is not assignable to type 'A & B'. + Type 'C & D' is not assignable to type 'A'. + Type 'D' is not assignable to type 'A'. +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(27,1): error TS2322: Type '(A & B) | (C & D)' is not assignable to type 'A | B'. + Type 'C & D' is not assignable to type 'A | B'. + Type 'C & D' is not assignable to type 'B'. + Type 'D' is not assignable to type 'B'. +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(28,1): error TS2322: Type '(A & B) | (C & D)' is not assignable to type 'C & D'. + Type 'A & B' is not assignable to type 'C & D'. + Type 'A & B' is not assignable to type 'C'. + Type 'B' is not assignable to type 'C'. +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(29,1): error TS2322: Type '(A & B) | (C & D)' is not assignable to type 'C | D'. + Type 'A & B' is not assignable to type 'C | D'. + Type 'A & B' is not assignable to type 'D'. + Type 'B' is not assignable to type 'D'. +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(31,1): error TS2322: Type 'A & B' is not assignable to type '(A | B) & (C | D)'. + Type 'A & B' is not assignable to type 'C | D'. + Type 'A & B' is not assignable to type 'D'. + Type 'B' is not assignable to type 'D'. +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(32,1): error TS2322: Type 'A | B' is not assignable to type '(A | B) & (C | D)'. + Type 'A' is not assignable to type '(A | B) & (C | D)'. + Type 'A' is not assignable to type 'C | D'. + Type 'A' is not assignable to type 'D'. +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(33,1): error TS2322: Type 'C & D' is not assignable to type '(A | B) & (C | D)'. + Type 'C & D' is not assignable to type 'A | B'. + Type 'C & D' is not assignable to type 'B'. + Type 'D' is not assignable to type 'B'. +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(34,1): error TS2322: Type 'C | D' is not assignable to type '(A | B) & (C | D)'. + Type 'C' is not assignable to type '(A | B) & (C | D)'. + Type 'C' is not assignable to type 'A | B'. + Type 'C' is not assignable to type 'B'. +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(35,1): error TS2322: Type '(A | B) & (C | D)' is not assignable to type 'A & B'. + Type '(A | B) & (C | D)' is not assignable to type 'A'. + Type 'C | D' is not assignable to type 'A'. + Type 'C' is not assignable to type 'A'. +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(37,1): error TS2322: Type '(A | B) & (C | D)' is not assignable to type 'C & D'. + Type '(A | B) & (C | D)' is not assignable to type 'C'. + Type 'C | D' is not assignable to type 'C'. + Type 'D' is not assignable to type 'C'. + + +==== tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts (14 errors) ==== + interface A { a: string } + interface B { b: string } + interface C { c: string } + interface D { d: string } + + var a: A; + var b: B; + var c: C; + var d: D; + var anb: A & B; + var aob: A | B; + var cnd: C & D; + var cod: C | D; + var x: A & B | C & D; + var y: (A | B) & (C | D); + + a = anb; // Ok + b = anb; // Ok + anb = a; + ~~~ +!!! error TS2322: Type 'A' is not assignable to type 'A & B'. +!!! error TS2322: Type 'A' is not assignable to type 'B'. + anb = b; + ~~~ +!!! error TS2322: Type 'B' is not assignable to type 'A & B'. +!!! error TS2322: Type 'B' is not assignable to type 'A'. +!!! error TS2322: Property 'a' is missing in type 'B'. + + x = anb; // Ok + x = aob; + ~ +!!! error TS2322: Type 'A | B' is not assignable to type '(A & B) | (C & D)'. +!!! error TS2322: Type 'A' is not assignable to type '(A & B) | (C & D)'. +!!! error TS2322: Type 'A' is not assignable to type 'C & D'. +!!! error TS2322: Type 'A' is not assignable to type 'C'. + x = cnd; // Ok + x = cod; + ~ +!!! error TS2322: Type 'C | D' is not assignable to type '(A & B) | (C & D)'. +!!! error TS2322: Type 'C' is not assignable to type '(A & B) | (C & D)'. +!!! error TS2322: Type 'C' is not assignable to type 'C & D'. +!!! error TS2322: Type 'C' is not assignable to type 'D'. + anb = x; + ~~~ +!!! error TS2322: Type '(A & B) | (C & D)' is not assignable to type 'A & B'. +!!! error TS2322: Type 'C & D' is not assignable to type 'A & B'. +!!! error TS2322: Type 'C & D' is not assignable to type 'A'. +!!! error TS2322: Type 'D' is not assignable to type 'A'. + aob = x; + ~~~ +!!! error TS2322: Type '(A & B) | (C & D)' is not assignable to type 'A | B'. +!!! error TS2322: Type 'C & D' is not assignable to type 'A | B'. +!!! error TS2322: Type 'C & D' is not assignable to type 'B'. +!!! error TS2322: Type 'D' is not assignable to type 'B'. + cnd = x; + ~~~ +!!! error TS2322: Type '(A & B) | (C & D)' is not assignable to type 'C & D'. +!!! error TS2322: Type 'A & B' is not assignable to type 'C & D'. +!!! error TS2322: Type 'A & B' is not assignable to type 'C'. +!!! error TS2322: Type 'B' is not assignable to type 'C'. + cod = x; + ~~~ +!!! error TS2322: Type '(A & B) | (C & D)' is not assignable to type 'C | D'. +!!! error TS2322: Type 'A & B' is not assignable to type 'C | D'. +!!! error TS2322: Type 'A & B' is not assignable to type 'D'. +!!! error TS2322: Type 'B' is not assignable to type 'D'. + + y = anb; + ~ +!!! error TS2322: Type 'A & B' is not assignable to type '(A | B) & (C | D)'. +!!! error TS2322: Type 'A & B' is not assignable to type 'C | D'. +!!! error TS2322: Type 'A & B' is not assignable to type 'D'. +!!! error TS2322: Type 'B' is not assignable to type 'D'. + y = aob; + ~ +!!! error TS2322: Type 'A | B' is not assignable to type '(A | B) & (C | D)'. +!!! error TS2322: Type 'A' is not assignable to type '(A | B) & (C | D)'. +!!! error TS2322: Type 'A' is not assignable to type 'C | D'. +!!! error TS2322: Type 'A' is not assignable to type 'D'. + y = cnd; + ~ +!!! error TS2322: Type 'C & D' is not assignable to type '(A | B) & (C | D)'. +!!! error TS2322: Type 'C & D' is not assignable to type 'A | B'. +!!! error TS2322: Type 'C & D' is not assignable to type 'B'. +!!! error TS2322: Type 'D' is not assignable to type 'B'. + y = cod; + ~ +!!! error TS2322: Type 'C | D' is not assignable to type '(A | B) & (C | D)'. +!!! error TS2322: Type 'C' is not assignable to type '(A | B) & (C | D)'. +!!! error TS2322: Type 'C' is not assignable to type 'A | B'. +!!! error TS2322: Type 'C' is not assignable to type 'B'. + anb = y; + ~~~ +!!! error TS2322: Type '(A | B) & (C | D)' is not assignable to type 'A & B'. +!!! error TS2322: Type '(A | B) & (C | D)' is not assignable to type 'A'. +!!! error TS2322: Type 'C | D' is not assignable to type 'A'. +!!! error TS2322: Type 'C' is not assignable to type 'A'. + aob = y; // Ok + cnd = y; + ~~~ +!!! error TS2322: Type '(A | B) & (C | D)' is not assignable to type 'C & D'. +!!! error TS2322: Type '(A | B) & (C | D)' is not assignable to type 'C'. +!!! error TS2322: Type 'C | D' is not assignable to type 'C'. +!!! error TS2322: Type 'D' is not assignable to type 'C'. + cod = y; // Ok + \ No newline at end of file diff --git a/tests/baselines/reference/intersectionAndUnionTypes.js b/tests/baselines/reference/intersectionAndUnionTypes.js new file mode 100644 index 0000000000..ec0adaab8b --- /dev/null +++ b/tests/baselines/reference/intersectionAndUnionTypes.js @@ -0,0 +1,72 @@ +//// [intersectionAndUnionTypes.ts] +interface A { a: string } +interface B { b: string } +interface C { c: string } +interface D { d: string } + +var a: A; +var b: B; +var c: C; +var d: D; +var anb: A & B; +var aob: A | B; +var cnd: C & D; +var cod: C | D; +var x: A & B | C & D; +var y: (A | B) & (C | D); + +a = anb; // Ok +b = anb; // Ok +anb = a; +anb = b; + +x = anb; // Ok +x = aob; +x = cnd; // Ok +x = cod; +anb = x; +aob = x; +cnd = x; +cod = x; + +y = anb; +y = aob; +y = cnd; +y = cod; +anb = y; +aob = y; // Ok +cnd = y; +cod = y; // Ok + + +//// [intersectionAndUnionTypes.js] +var a; +var b; +var c; +var d; +var anb; +var aob; +var cnd; +var cod; +var x; +var y; +a = anb; // Ok +b = anb; // Ok +anb = a; +anb = b; +x = anb; // Ok +x = aob; +x = cnd; // Ok +x = cod; +anb = x; +aob = x; +cnd = x; +cod = x; +y = anb; +y = aob; +y = cnd; +y = cod; +anb = y; +aob = y; // Ok +cnd = y; +cod = y; // Ok diff --git a/tests/baselines/reference/intersectionTypeAssignment.errors.txt b/tests/baselines/reference/intersectionTypeAssignment.errors.txt new file mode 100644 index 0000000000..11ea91d8f0 --- /dev/null +++ b/tests/baselines/reference/intersectionTypeAssignment.errors.txt @@ -0,0 +1,45 @@ +tests/cases/conformance/types/intersection/intersectionTypeAssignment.ts(8,1): error TS2322: Type '{ a: string; }' is not assignable to type '{ a: string; b: string; }'. + Property 'b' is missing in type '{ a: string; }'. +tests/cases/conformance/types/intersection/intersectionTypeAssignment.ts(9,1): error TS2322: Type '{ a: string; }' is not assignable to type '{ a: string; } & { b: string; }'. + Type '{ a: string; }' is not assignable to type '{ b: string; }'. + Property 'b' is missing in type '{ a: string; }'. +tests/cases/conformance/types/intersection/intersectionTypeAssignment.ts(13,1): error TS2322: Type '{ b: string; }' is not assignable to type '{ a: string; b: string; }'. + Property 'a' is missing in type '{ b: string; }'. +tests/cases/conformance/types/intersection/intersectionTypeAssignment.ts(14,1): error TS2322: Type '{ b: string; }' is not assignable to type '{ a: string; } & { b: string; }'. + Type '{ b: string; }' is not assignable to type '{ a: string; }'. + Property 'a' is missing in type '{ b: string; }'. + + +==== tests/cases/conformance/types/intersection/intersectionTypeAssignment.ts (4 errors) ==== + var a: { a: string }; + var b: { b: string }; + var x: { a: string, b: string }; + var y: { a: string } & { b: string }; + + a = x; + a = y; + x = a; // Error + ~ +!!! error TS2322: Type '{ a: string; }' is not assignable to type '{ a: string; b: string; }'. +!!! error TS2322: Property 'b' is missing in type '{ a: string; }'. + y = a; // Error + ~ +!!! error TS2322: Type '{ a: string; }' is not assignable to type '{ a: string; } & { b: string; }'. +!!! error TS2322: Type '{ a: string; }' is not assignable to type '{ b: string; }'. +!!! error TS2322: Property 'b' is missing in type '{ a: string; }'. + + b = x; + b = y; + x = b; // Error + ~ +!!! error TS2322: Type '{ b: string; }' is not assignable to type '{ a: string; b: string; }'. +!!! error TS2322: Property 'a' is missing in type '{ b: string; }'. + y = b; // Error + ~ +!!! error TS2322: Type '{ b: string; }' is not assignable to type '{ a: string; } & { b: string; }'. +!!! error TS2322: Type '{ b: string; }' is not assignable to type '{ a: string; }'. +!!! error TS2322: Property 'a' is missing in type '{ b: string; }'. + + x = y; + y = x; + \ No newline at end of file diff --git a/tests/baselines/reference/intersectionTypeAssignment.js b/tests/baselines/reference/intersectionTypeAssignment.js new file mode 100644 index 0000000000..f1cddbf3b2 --- /dev/null +++ b/tests/baselines/reference/intersectionTypeAssignment.js @@ -0,0 +1,35 @@ +//// [intersectionTypeAssignment.ts] +var a: { a: string }; +var b: { b: string }; +var x: { a: string, b: string }; +var y: { a: string } & { b: string }; + +a = x; +a = y; +x = a; // Error +y = a; // Error + +b = x; +b = y; +x = b; // Error +y = b; // Error + +x = y; +y = x; + + +//// [intersectionTypeAssignment.js] +var a; +var b; +var x; +var y; +a = x; +a = y; +x = a; // Error +y = a; // Error +b = x; +b = y; +x = b; // Error +y = b; // Error +x = y; +y = x; diff --git a/tests/baselines/reference/intersectionTypeEquivalence.js b/tests/baselines/reference/intersectionTypeEquivalence.js new file mode 100644 index 0000000000..be04bfe794 --- /dev/null +++ b/tests/baselines/reference/intersectionTypeEquivalence.js @@ -0,0 +1,31 @@ +//// [intersectionTypeEquivalence.ts] +interface A { a: string } +interface B { b: string } +interface C { c: string } + +// A & B is equivalent to B & A. +var y: A & B; +var y : B & A; + +// AB & C is equivalent to A & BC, where AB is A & B and BC is B & C. +var z : A & B & C; +var z : (A & B) & C; +var z : A & (B & C); +var ab : A & B; +var bc : B & C; +var z1: typeof ab & C; +var z1: A & typeof bc; + + +//// [intersectionTypeEquivalence.js] +// A & B is equivalent to B & A. +var y; +var y; +// AB & C is equivalent to A & BC, where AB is A & B and BC is B & C. +var z; +var z; +var z; +var ab; +var bc; +var z1; +var z1; diff --git a/tests/baselines/reference/intersectionTypeEquivalence.symbols b/tests/baselines/reference/intersectionTypeEquivalence.symbols new file mode 100644 index 0000000000..2bec452c44 --- /dev/null +++ b/tests/baselines/reference/intersectionTypeEquivalence.symbols @@ -0,0 +1,63 @@ +=== tests/cases/conformance/types/intersection/intersectionTypeEquivalence.ts === +interface A { a: string } +>A : Symbol(A, Decl(intersectionTypeEquivalence.ts, 0, 0)) +>a : Symbol(a, Decl(intersectionTypeEquivalence.ts, 0, 13)) + +interface B { b: string } +>B : Symbol(B, Decl(intersectionTypeEquivalence.ts, 0, 25)) +>b : Symbol(b, Decl(intersectionTypeEquivalence.ts, 1, 13)) + +interface C { c: string } +>C : Symbol(C, Decl(intersectionTypeEquivalence.ts, 1, 25)) +>c : Symbol(c, Decl(intersectionTypeEquivalence.ts, 2, 13)) + +// A & B is equivalent to B & A. +var y: A & B; +>y : Symbol(y, Decl(intersectionTypeEquivalence.ts, 5, 3), Decl(intersectionTypeEquivalence.ts, 6, 3)) +>A : Symbol(A, Decl(intersectionTypeEquivalence.ts, 0, 0)) +>B : Symbol(B, Decl(intersectionTypeEquivalence.ts, 0, 25)) + +var y : B & A; +>y : Symbol(y, Decl(intersectionTypeEquivalence.ts, 5, 3), Decl(intersectionTypeEquivalence.ts, 6, 3)) +>B : Symbol(B, Decl(intersectionTypeEquivalence.ts, 0, 25)) +>A : Symbol(A, Decl(intersectionTypeEquivalence.ts, 0, 0)) + +// AB & C is equivalent to A & BC, where AB is A & B and BC is B & C. +var z : A & B & C; +>z : Symbol(z, Decl(intersectionTypeEquivalence.ts, 9, 3), Decl(intersectionTypeEquivalence.ts, 10, 3), Decl(intersectionTypeEquivalence.ts, 11, 3)) +>A : Symbol(A, Decl(intersectionTypeEquivalence.ts, 0, 0)) +>B : Symbol(B, Decl(intersectionTypeEquivalence.ts, 0, 25)) +>C : Symbol(C, Decl(intersectionTypeEquivalence.ts, 1, 25)) + +var z : (A & B) & C; +>z : Symbol(z, Decl(intersectionTypeEquivalence.ts, 9, 3), Decl(intersectionTypeEquivalence.ts, 10, 3), Decl(intersectionTypeEquivalence.ts, 11, 3)) +>A : Symbol(A, Decl(intersectionTypeEquivalence.ts, 0, 0)) +>B : Symbol(B, Decl(intersectionTypeEquivalence.ts, 0, 25)) +>C : Symbol(C, Decl(intersectionTypeEquivalence.ts, 1, 25)) + +var z : A & (B & C); +>z : Symbol(z, Decl(intersectionTypeEquivalence.ts, 9, 3), Decl(intersectionTypeEquivalence.ts, 10, 3), Decl(intersectionTypeEquivalence.ts, 11, 3)) +>A : Symbol(A, Decl(intersectionTypeEquivalence.ts, 0, 0)) +>B : Symbol(B, Decl(intersectionTypeEquivalence.ts, 0, 25)) +>C : Symbol(C, Decl(intersectionTypeEquivalence.ts, 1, 25)) + +var ab : A & B; +>ab : Symbol(ab, Decl(intersectionTypeEquivalence.ts, 12, 3)) +>A : Symbol(A, Decl(intersectionTypeEquivalence.ts, 0, 0)) +>B : Symbol(B, Decl(intersectionTypeEquivalence.ts, 0, 25)) + +var bc : B & C; +>bc : Symbol(bc, Decl(intersectionTypeEquivalence.ts, 13, 3)) +>B : Symbol(B, Decl(intersectionTypeEquivalence.ts, 0, 25)) +>C : Symbol(C, Decl(intersectionTypeEquivalence.ts, 1, 25)) + +var z1: typeof ab & C; +>z1 : Symbol(z1, Decl(intersectionTypeEquivalence.ts, 14, 3), Decl(intersectionTypeEquivalence.ts, 15, 3)) +>ab : Symbol(ab, Decl(intersectionTypeEquivalence.ts, 12, 3)) +>C : Symbol(C, Decl(intersectionTypeEquivalence.ts, 1, 25)) + +var z1: A & typeof bc; +>z1 : Symbol(z1, Decl(intersectionTypeEquivalence.ts, 14, 3), Decl(intersectionTypeEquivalence.ts, 15, 3)) +>A : Symbol(A, Decl(intersectionTypeEquivalence.ts, 0, 0)) +>bc : Symbol(bc, Decl(intersectionTypeEquivalence.ts, 13, 3)) + diff --git a/tests/baselines/reference/intersectionTypeEquivalence.types b/tests/baselines/reference/intersectionTypeEquivalence.types new file mode 100644 index 0000000000..25e175275d --- /dev/null +++ b/tests/baselines/reference/intersectionTypeEquivalence.types @@ -0,0 +1,63 @@ +=== tests/cases/conformance/types/intersection/intersectionTypeEquivalence.ts === +interface A { a: string } +>A : A +>a : string + +interface B { b: string } +>B : B +>b : string + +interface C { c: string } +>C : C +>c : string + +// A & B is equivalent to B & A. +var y: A & B; +>y : A & B +>A : A +>B : B + +var y : B & A; +>y : A & B +>B : B +>A : A + +// AB & C is equivalent to A & BC, where AB is A & B and BC is B & C. +var z : A & B & C; +>z : A & B & C +>A : A +>B : B +>C : C + +var z : (A & B) & C; +>z : A & B & C +>A : A +>B : B +>C : C + +var z : A & (B & C); +>z : A & B & C +>A : A +>B : B +>C : C + +var ab : A & B; +>ab : A & B +>A : A +>B : B + +var bc : B & C; +>bc : B & C +>B : B +>C : C + +var z1: typeof ab & C; +>z1 : A & B & C +>ab : A & B +>C : C + +var z1: A & typeof bc; +>z1 : A & B & C +>A : A +>bc : B & C + diff --git a/tests/baselines/reference/intersectionTypeInference.errors.txt b/tests/baselines/reference/intersectionTypeInference.errors.txt new file mode 100644 index 0000000000..af0b1b447b --- /dev/null +++ b/tests/baselines/reference/intersectionTypeInference.errors.txt @@ -0,0 +1,41 @@ +tests/cases/conformance/types/intersection/intersectionTypeInference.ts(5,5): error TS2322: Type 'T' is not assignable to type 'T & U'. + Type 'T' is not assignable to type 'U'. +tests/cases/conformance/types/intersection/intersectionTypeInference.ts(6,5): error TS2322: Type 'U' is not assignable to type 'T & U'. + Type 'U' is not assignable to type 'T'. + + +==== tests/cases/conformance/types/intersection/intersectionTypeInference.ts (2 errors) ==== + function extend(obj1: T, obj2: U): T & U { + var result: T & U; + obj1 = result; + obj2 = result; + result = obj1; // Error + ~~~~~~ +!!! error TS2322: Type 'T' is not assignable to type 'T & U'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. + result = obj2; // Error + ~~~~~~ +!!! error TS2322: Type 'U' is not assignable to type 'T & U'. +!!! error TS2322: Type 'U' is not assignable to type 'T'. + return result; + } + + var x = extend({ a: "hello" }, { b: 42 }); + var s = x.a; + var n = x.b; + + interface A { + a: T; + } + + interface B { + b: U; + } + + function foo(obj: A & B): T | U { + return undefined; + } + + var z = foo({ a: "hello", b: 42 }); + var z: string | number; + \ No newline at end of file diff --git a/tests/baselines/reference/intersectionTypeInference.js b/tests/baselines/reference/intersectionTypeInference.js new file mode 100644 index 0000000000..8e7f1238c0 --- /dev/null +++ b/tests/baselines/reference/intersectionTypeInference.js @@ -0,0 +1,47 @@ +//// [intersectionTypeInference.ts] +function extend(obj1: T, obj2: U): T & U { + var result: T & U; + obj1 = result; + obj2 = result; + result = obj1; // Error + result = obj2; // Error + return result; +} + +var x = extend({ a: "hello" }, { b: 42 }); +var s = x.a; +var n = x.b; + +interface A { + a: T; +} + +interface B { + b: U; +} + +function foo(obj: A & B): T | U { + return undefined; +} + +var z = foo({ a: "hello", b: 42 }); +var z: string | number; + + +//// [intersectionTypeInference.js] +function extend(obj1, obj2) { + var result; + obj1 = result; + obj2 = result; + result = obj1; // Error + result = obj2; // Error + return result; +} +var x = extend({ a: "hello" }, { b: 42 }); +var s = x.a; +var n = x.b; +function foo(obj) { + return undefined; +} +var z = foo({ a: "hello", b: 42 }); +var z; diff --git a/tests/baselines/reference/intersectionTypeMembers.js b/tests/baselines/reference/intersectionTypeMembers.js new file mode 100644 index 0000000000..9c1cd75d60 --- /dev/null +++ b/tests/baselines/reference/intersectionTypeMembers.js @@ -0,0 +1,44 @@ +//// [intersectionTypeMembers.ts] +// An intersection type has those members that are present in any of its constituent types, +// with types that are intersections of the respective members in the constituent types + +interface A { a: string } +interface B { b: string } +interface C { c: string } + +var abc: A & B & C; +abc.a = "hello"; +abc.b = "hello"; +abc.c = "hello"; + +interface X { x: A } +interface Y { x: B } +interface Z { x: C } + +var xyz: X & Y & Z; +xyz.x.a = "hello"; +xyz.x.b = "hello"; +xyz.x.c = "hello"; + +type F1 = (x: string) => string; +type F2 = (x: number) => number; + +var f: F1 & F2; +var s = f("hello"); +var n = f(42); + + +//// [intersectionTypeMembers.js] +// An intersection type has those members that are present in any of its constituent types, +// with types that are intersections of the respective members in the constituent types +var abc; +abc.a = "hello"; +abc.b = "hello"; +abc.c = "hello"; +var xyz; +xyz.x.a = "hello"; +xyz.x.b = "hello"; +xyz.x.c = "hello"; +var f; +var s = f("hello"); +var n = f(42); diff --git a/tests/baselines/reference/intersectionTypeMembers.symbols b/tests/baselines/reference/intersectionTypeMembers.symbols new file mode 100644 index 0000000000..ebec69b4eb --- /dev/null +++ b/tests/baselines/reference/intersectionTypeMembers.symbols @@ -0,0 +1,100 @@ +=== tests/cases/conformance/types/intersection/intersectionTypeMembers.ts === +// An intersection type has those members that are present in any of its constituent types, +// with types that are intersections of the respective members in the constituent types + +interface A { a: string } +>A : Symbol(A, Decl(intersectionTypeMembers.ts, 0, 0)) +>a : Symbol(a, Decl(intersectionTypeMembers.ts, 3, 13)) + +interface B { b: string } +>B : Symbol(B, Decl(intersectionTypeMembers.ts, 3, 25)) +>b : Symbol(b, Decl(intersectionTypeMembers.ts, 4, 13)) + +interface C { c: string } +>C : Symbol(C, Decl(intersectionTypeMembers.ts, 4, 25)) +>c : Symbol(c, Decl(intersectionTypeMembers.ts, 5, 13)) + +var abc: A & B & C; +>abc : Symbol(abc, Decl(intersectionTypeMembers.ts, 7, 3)) +>A : Symbol(A, Decl(intersectionTypeMembers.ts, 0, 0)) +>B : Symbol(B, Decl(intersectionTypeMembers.ts, 3, 25)) +>C : Symbol(C, Decl(intersectionTypeMembers.ts, 4, 25)) + +abc.a = "hello"; +>abc.a : Symbol(A.a, Decl(intersectionTypeMembers.ts, 3, 13)) +>abc : Symbol(abc, Decl(intersectionTypeMembers.ts, 7, 3)) +>a : Symbol(A.a, Decl(intersectionTypeMembers.ts, 3, 13)) + +abc.b = "hello"; +>abc.b : Symbol(B.b, Decl(intersectionTypeMembers.ts, 4, 13)) +>abc : Symbol(abc, Decl(intersectionTypeMembers.ts, 7, 3)) +>b : Symbol(B.b, Decl(intersectionTypeMembers.ts, 4, 13)) + +abc.c = "hello"; +>abc.c : Symbol(C.c, Decl(intersectionTypeMembers.ts, 5, 13)) +>abc : Symbol(abc, Decl(intersectionTypeMembers.ts, 7, 3)) +>c : Symbol(C.c, Decl(intersectionTypeMembers.ts, 5, 13)) + +interface X { x: A } +>X : Symbol(X, Decl(intersectionTypeMembers.ts, 10, 16)) +>x : Symbol(x, Decl(intersectionTypeMembers.ts, 12, 13)) +>A : Symbol(A, Decl(intersectionTypeMembers.ts, 0, 0)) + +interface Y { x: B } +>Y : Symbol(Y, Decl(intersectionTypeMembers.ts, 12, 20)) +>x : Symbol(x, Decl(intersectionTypeMembers.ts, 13, 13)) +>B : Symbol(B, Decl(intersectionTypeMembers.ts, 3, 25)) + +interface Z { x: C } +>Z : Symbol(Z, Decl(intersectionTypeMembers.ts, 13, 20)) +>x : Symbol(x, Decl(intersectionTypeMembers.ts, 14, 13)) +>C : Symbol(C, Decl(intersectionTypeMembers.ts, 4, 25)) + +var xyz: X & Y & Z; +>xyz : Symbol(xyz, Decl(intersectionTypeMembers.ts, 16, 3)) +>X : Symbol(X, Decl(intersectionTypeMembers.ts, 10, 16)) +>Y : Symbol(Y, Decl(intersectionTypeMembers.ts, 12, 20)) +>Z : Symbol(Z, Decl(intersectionTypeMembers.ts, 13, 20)) + +xyz.x.a = "hello"; +>xyz.x.a : Symbol(A.a, Decl(intersectionTypeMembers.ts, 3, 13)) +>xyz.x : Symbol(x, Decl(intersectionTypeMembers.ts, 12, 13), Decl(intersectionTypeMembers.ts, 13, 13), Decl(intersectionTypeMembers.ts, 14, 13)) +>xyz : Symbol(xyz, Decl(intersectionTypeMembers.ts, 16, 3)) +>x : Symbol(x, Decl(intersectionTypeMembers.ts, 12, 13), Decl(intersectionTypeMembers.ts, 13, 13), Decl(intersectionTypeMembers.ts, 14, 13)) +>a : Symbol(A.a, Decl(intersectionTypeMembers.ts, 3, 13)) + +xyz.x.b = "hello"; +>xyz.x.b : Symbol(B.b, Decl(intersectionTypeMembers.ts, 4, 13)) +>xyz.x : Symbol(x, Decl(intersectionTypeMembers.ts, 12, 13), Decl(intersectionTypeMembers.ts, 13, 13), Decl(intersectionTypeMembers.ts, 14, 13)) +>xyz : Symbol(xyz, Decl(intersectionTypeMembers.ts, 16, 3)) +>x : Symbol(x, Decl(intersectionTypeMembers.ts, 12, 13), Decl(intersectionTypeMembers.ts, 13, 13), Decl(intersectionTypeMembers.ts, 14, 13)) +>b : Symbol(B.b, Decl(intersectionTypeMembers.ts, 4, 13)) + +xyz.x.c = "hello"; +>xyz.x.c : Symbol(C.c, Decl(intersectionTypeMembers.ts, 5, 13)) +>xyz.x : Symbol(x, Decl(intersectionTypeMembers.ts, 12, 13), Decl(intersectionTypeMembers.ts, 13, 13), Decl(intersectionTypeMembers.ts, 14, 13)) +>xyz : Symbol(xyz, Decl(intersectionTypeMembers.ts, 16, 3)) +>x : Symbol(x, Decl(intersectionTypeMembers.ts, 12, 13), Decl(intersectionTypeMembers.ts, 13, 13), Decl(intersectionTypeMembers.ts, 14, 13)) +>c : Symbol(C.c, Decl(intersectionTypeMembers.ts, 5, 13)) + +type F1 = (x: string) => string; +>F1 : Symbol(F1, Decl(intersectionTypeMembers.ts, 19, 18)) +>x : Symbol(x, Decl(intersectionTypeMembers.ts, 21, 11)) + +type F2 = (x: number) => number; +>F2 : Symbol(F2, Decl(intersectionTypeMembers.ts, 21, 32)) +>x : Symbol(x, Decl(intersectionTypeMembers.ts, 22, 11)) + +var f: F1 & F2; +>f : Symbol(f, Decl(intersectionTypeMembers.ts, 24, 3)) +>F1 : Symbol(F1, Decl(intersectionTypeMembers.ts, 19, 18)) +>F2 : Symbol(F2, Decl(intersectionTypeMembers.ts, 21, 32)) + +var s = f("hello"); +>s : Symbol(s, Decl(intersectionTypeMembers.ts, 25, 3)) +>f : Symbol(f, Decl(intersectionTypeMembers.ts, 24, 3)) + +var n = f(42); +>n : Symbol(n, Decl(intersectionTypeMembers.ts, 26, 3)) +>f : Symbol(f, Decl(intersectionTypeMembers.ts, 24, 3)) + diff --git a/tests/baselines/reference/intersectionTypeMembers.types b/tests/baselines/reference/intersectionTypeMembers.types new file mode 100644 index 0000000000..a97a54c7a8 --- /dev/null +++ b/tests/baselines/reference/intersectionTypeMembers.types @@ -0,0 +1,116 @@ +=== tests/cases/conformance/types/intersection/intersectionTypeMembers.ts === +// An intersection type has those members that are present in any of its constituent types, +// with types that are intersections of the respective members in the constituent types + +interface A { a: string } +>A : A +>a : string + +interface B { b: string } +>B : B +>b : string + +interface C { c: string } +>C : C +>c : string + +var abc: A & B & C; +>abc : A & B & C +>A : A +>B : B +>C : C + +abc.a = "hello"; +>abc.a = "hello" : string +>abc.a : string +>abc : A & B & C +>a : string +>"hello" : string + +abc.b = "hello"; +>abc.b = "hello" : string +>abc.b : string +>abc : A & B & C +>b : string +>"hello" : string + +abc.c = "hello"; +>abc.c = "hello" : string +>abc.c : string +>abc : A & B & C +>c : string +>"hello" : string + +interface X { x: A } +>X : X +>x : A +>A : A + +interface Y { x: B } +>Y : Y +>x : B +>B : B + +interface Z { x: C } +>Z : Z +>x : C +>C : C + +var xyz: X & Y & Z; +>xyz : X & Y & Z +>X : X +>Y : Y +>Z : Z + +xyz.x.a = "hello"; +>xyz.x.a = "hello" : string +>xyz.x.a : string +>xyz.x : A & B & C +>xyz : X & Y & Z +>x : A & B & C +>a : string +>"hello" : string + +xyz.x.b = "hello"; +>xyz.x.b = "hello" : string +>xyz.x.b : string +>xyz.x : A & B & C +>xyz : X & Y & Z +>x : A & B & C +>b : string +>"hello" : string + +xyz.x.c = "hello"; +>xyz.x.c = "hello" : string +>xyz.x.c : string +>xyz.x : A & B & C +>xyz : X & Y & Z +>x : A & B & C +>c : string +>"hello" : string + +type F1 = (x: string) => string; +>F1 : (x: string) => string +>x : string + +type F2 = (x: number) => number; +>F2 : (x: number) => number +>x : number + +var f: F1 & F2; +>f : ((x: string) => string) & ((x: number) => number) +>F1 : (x: string) => string +>F2 : (x: number) => number + +var s = f("hello"); +>s : string +>f("hello") : string +>f : ((x: string) => string) & ((x: number) => number) +>"hello" : string + +var n = f(42); +>n : number +>f(42) : number +>f : ((x: string) => string) & ((x: number) => number) +>42 : number + diff --git a/tests/baselines/reference/intersectionTypeOverloading.js b/tests/baselines/reference/intersectionTypeOverloading.js new file mode 100644 index 0000000000..b7080e449b --- /dev/null +++ b/tests/baselines/reference/intersectionTypeOverloading.js @@ -0,0 +1,26 @@ +//// [intersectionTypeOverloading.ts] +// Check that order is preserved in intersection types for purposes of +// overload resolution + +type F = (s: string) => string; +type G = (x: any) => any; + +var fg: F & G; +var gf: G & F; + +var x = fg("abc"); +var x: string; + +var y = gf("abc"); +var y: any; + + +//// [intersectionTypeOverloading.js] +// Check that order is preserved in intersection types for purposes of +// overload resolution +var fg; +var gf; +var x = fg("abc"); +var x; +var y = gf("abc"); +var y; diff --git a/tests/baselines/reference/intersectionTypeOverloading.symbols b/tests/baselines/reference/intersectionTypeOverloading.symbols new file mode 100644 index 0000000000..c850334504 --- /dev/null +++ b/tests/baselines/reference/intersectionTypeOverloading.symbols @@ -0,0 +1,36 @@ +=== tests/cases/conformance/types/intersection/intersectionTypeOverloading.ts === +// Check that order is preserved in intersection types for purposes of +// overload resolution + +type F = (s: string) => string; +>F : Symbol(F, Decl(intersectionTypeOverloading.ts, 0, 0)) +>s : Symbol(s, Decl(intersectionTypeOverloading.ts, 3, 10)) + +type G = (x: any) => any; +>G : Symbol(G, Decl(intersectionTypeOverloading.ts, 3, 31)) +>x : Symbol(x, Decl(intersectionTypeOverloading.ts, 4, 10)) + +var fg: F & G; +>fg : Symbol(fg, Decl(intersectionTypeOverloading.ts, 6, 3)) +>F : Symbol(F, Decl(intersectionTypeOverloading.ts, 0, 0)) +>G : Symbol(G, Decl(intersectionTypeOverloading.ts, 3, 31)) + +var gf: G & F; +>gf : Symbol(gf, Decl(intersectionTypeOverloading.ts, 7, 3)) +>G : Symbol(G, Decl(intersectionTypeOverloading.ts, 3, 31)) +>F : Symbol(F, Decl(intersectionTypeOverloading.ts, 0, 0)) + +var x = fg("abc"); +>x : Symbol(x, Decl(intersectionTypeOverloading.ts, 9, 3), Decl(intersectionTypeOverloading.ts, 10, 3)) +>fg : Symbol(fg, Decl(intersectionTypeOverloading.ts, 6, 3)) + +var x: string; +>x : Symbol(x, Decl(intersectionTypeOverloading.ts, 9, 3), Decl(intersectionTypeOverloading.ts, 10, 3)) + +var y = gf("abc"); +>y : Symbol(y, Decl(intersectionTypeOverloading.ts, 12, 3), Decl(intersectionTypeOverloading.ts, 13, 3)) +>gf : Symbol(gf, Decl(intersectionTypeOverloading.ts, 7, 3)) + +var y: any; +>y : Symbol(y, Decl(intersectionTypeOverloading.ts, 12, 3), Decl(intersectionTypeOverloading.ts, 13, 3)) + diff --git a/tests/baselines/reference/intersectionTypeOverloading.types b/tests/baselines/reference/intersectionTypeOverloading.types new file mode 100644 index 0000000000..c478b6a08c --- /dev/null +++ b/tests/baselines/reference/intersectionTypeOverloading.types @@ -0,0 +1,40 @@ +=== tests/cases/conformance/types/intersection/intersectionTypeOverloading.ts === +// Check that order is preserved in intersection types for purposes of +// overload resolution + +type F = (s: string) => string; +>F : (s: string) => string +>s : string + +type G = (x: any) => any; +>G : (x: any) => any +>x : any + +var fg: F & G; +>fg : ((s: string) => string) & ((x: any) => any) +>F : (s: string) => string +>G : (x: any) => any + +var gf: G & F; +>gf : ((x: any) => any) & ((s: string) => string) +>G : (x: any) => any +>F : (s: string) => string + +var x = fg("abc"); +>x : string +>fg("abc") : string +>fg : ((s: string) => string) & ((x: any) => any) +>"abc" : string + +var x: string; +>x : string + +var y = gf("abc"); +>y : any +>gf("abc") : any +>gf : ((x: any) => any) & ((s: string) => string) +>"abc" : string + +var y: any; +>y : any + diff --git a/tests/baselines/reference/recursiveIntersectionTypes.errors.txt b/tests/baselines/reference/recursiveIntersectionTypes.errors.txt new file mode 100644 index 0000000000..bac97470e5 --- /dev/null +++ b/tests/baselines/reference/recursiveIntersectionTypes.errors.txt @@ -0,0 +1,32 @@ +tests/cases/conformance/types/intersection/recursiveIntersectionTypes.ts(19,1): error TS2322: Type 'Entity & { next: Entity & any; }' is not assignable to type 'Product & { next: Product & any; }'. + Type 'Entity & { next: Entity & any; }' is not assignable to type 'Product'. + Type '{ next: Entity & any; }' is not assignable to type 'Product'. + Property 'price' is missing in type '{ next: Entity & any; }'. + + +==== tests/cases/conformance/types/intersection/recursiveIntersectionTypes.ts (1 errors) ==== + type LinkedList = T & { next: LinkedList }; + + interface Entity { + name: string; + } + + interface Product extends Entity { + price: number; + } + + var entityList: LinkedList; + var s = entityList.name; + var s = entityList.next.name; + var s = entityList.next.next.name; + var s = entityList.next.next.next.name; + + var productList: LinkedList; + entityList = productList; + productList = entityList; // Error + ~~~~~~~~~~~ +!!! error TS2322: Type 'Entity & { next: Entity & any; }' is not assignable to type 'Product & { next: Product & any; }'. +!!! error TS2322: Type 'Entity & { next: Entity & any; }' is not assignable to type 'Product'. +!!! error TS2322: Type '{ next: Entity & any; }' is not assignable to type 'Product'. +!!! error TS2322: Property 'price' is missing in type '{ next: Entity & any; }'. + \ No newline at end of file diff --git a/tests/baselines/reference/recursiveIntersectionTypes.js b/tests/baselines/reference/recursiveIntersectionTypes.js new file mode 100644 index 0000000000..88b7e6f831 --- /dev/null +++ b/tests/baselines/reference/recursiveIntersectionTypes.js @@ -0,0 +1,31 @@ +//// [recursiveIntersectionTypes.ts] +type LinkedList = T & { next: LinkedList }; + +interface Entity { + name: string; +} + +interface Product extends Entity { + price: number; +} + +var entityList: LinkedList; +var s = entityList.name; +var s = entityList.next.name; +var s = entityList.next.next.name; +var s = entityList.next.next.next.name; + +var productList: LinkedList; +entityList = productList; +productList = entityList; // Error + + +//// [recursiveIntersectionTypes.js] +var entityList; +var s = entityList.name; +var s = entityList.next.name; +var s = entityList.next.next.name; +var s = entityList.next.next.next.name; +var productList; +entityList = productList; +productList = entityList; // Error diff --git a/tests/baselines/reference/typeParameterExtendingUnion1.symbols b/tests/baselines/reference/typeParameterExtendingUnion1.symbols index 39b67e6428..97d900b5d5 100644 --- a/tests/baselines/reference/typeParameterExtendingUnion1.symbols +++ b/tests/baselines/reference/typeParameterExtendingUnion1.symbols @@ -33,9 +33,9 @@ function f(a: T) { >T : Symbol(T, Decl(typeParameterExtendingUnion1.ts, 8, 11)) a.run(); ->a.run : Symbol(run, Decl(typeParameterExtendingUnion1.ts, 0, 14), Decl(typeParameterExtendingUnion1.ts, 0, 14)) +>a.run : Symbol(Animal.run, Decl(typeParameterExtendingUnion1.ts, 0, 14)) >a : Symbol(a, Decl(typeParameterExtendingUnion1.ts, 8, 32)) ->run : Symbol(run, Decl(typeParameterExtendingUnion1.ts, 0, 14), Decl(typeParameterExtendingUnion1.ts, 0, 14)) +>run : Symbol(Animal.run, Decl(typeParameterExtendingUnion1.ts, 0, 14)) run(a); >run : Symbol(run, Decl(typeParameterExtendingUnion1.ts, 2, 33)) diff --git a/tests/baselines/reference/typeParameterExtendingUnion2.symbols b/tests/baselines/reference/typeParameterExtendingUnion2.symbols index 44d47692a8..21866b3df9 100644 --- a/tests/baselines/reference/typeParameterExtendingUnion2.symbols +++ b/tests/baselines/reference/typeParameterExtendingUnion2.symbols @@ -20,9 +20,9 @@ function run(a: Cat | Dog) { >Dog : Symbol(Dog, Decl(typeParameterExtendingUnion2.ts, 1, 33)) a.run(); ->a.run : Symbol(run, Decl(typeParameterExtendingUnion2.ts, 0, 14), Decl(typeParameterExtendingUnion2.ts, 0, 14)) +>a.run : Symbol(Animal.run, Decl(typeParameterExtendingUnion2.ts, 0, 14)) >a : Symbol(a, Decl(typeParameterExtendingUnion2.ts, 4, 13)) ->run : Symbol(run, Decl(typeParameterExtendingUnion2.ts, 0, 14), Decl(typeParameterExtendingUnion2.ts, 0, 14)) +>run : Symbol(Animal.run, Decl(typeParameterExtendingUnion2.ts, 0, 14)) } function f(a: T) { @@ -34,9 +34,9 @@ function f(a: T) { >T : Symbol(T, Decl(typeParameterExtendingUnion2.ts, 8, 11)) a.run(); ->a.run : Symbol(run, Decl(typeParameterExtendingUnion2.ts, 0, 14), Decl(typeParameterExtendingUnion2.ts, 0, 14)) +>a.run : Symbol(Animal.run, Decl(typeParameterExtendingUnion2.ts, 0, 14)) >a : Symbol(a, Decl(typeParameterExtendingUnion2.ts, 8, 32)) ->run : Symbol(run, Decl(typeParameterExtendingUnion2.ts, 0, 14), Decl(typeParameterExtendingUnion2.ts, 0, 14)) +>run : Symbol(Animal.run, Decl(typeParameterExtendingUnion2.ts, 0, 14)) run(a); >run : Symbol(run, Decl(typeParameterExtendingUnion2.ts, 2, 33)) diff --git a/tests/cases/conformance/types/intersection/contextualIntersectionType.ts b/tests/cases/conformance/types/intersection/contextualIntersectionType.ts new file mode 100644 index 0000000000..580827992a --- /dev/null +++ b/tests/cases/conformance/types/intersection/contextualIntersectionType.ts @@ -0,0 +1,5 @@ +var x: { a: (s: string) => string } & { b: (n: number) => number }; +x = { + a: s => s, + b: n => n +}; diff --git a/tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts b/tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts new file mode 100644 index 0000000000..e6bd671c6b --- /dev/null +++ b/tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts @@ -0,0 +1,38 @@ +interface A { a: string } +interface B { b: string } +interface C { c: string } +interface D { d: string } + +var a: A; +var b: B; +var c: C; +var d: D; +var anb: A & B; +var aob: A | B; +var cnd: C & D; +var cod: C | D; +var x: A & B | C & D; +var y: (A | B) & (C | D); + +a = anb; // Ok +b = anb; // Ok +anb = a; +anb = b; + +x = anb; // Ok +x = aob; +x = cnd; // Ok +x = cod; +anb = x; +aob = x; +cnd = x; +cod = x; + +y = anb; +y = aob; +y = cnd; +y = cod; +anb = y; +aob = y; // Ok +cnd = y; +cod = y; // Ok diff --git a/tests/cases/conformance/types/intersection/intersectionTypeAssignment.ts b/tests/cases/conformance/types/intersection/intersectionTypeAssignment.ts new file mode 100644 index 0000000000..00a299ea24 --- /dev/null +++ b/tests/cases/conformance/types/intersection/intersectionTypeAssignment.ts @@ -0,0 +1,17 @@ +var a: { a: string }; +var b: { b: string }; +var x: { a: string, b: string }; +var y: { a: string } & { b: string }; + +a = x; +a = y; +x = a; // Error +y = a; // Error + +b = x; +b = y; +x = b; // Error +y = b; // Error + +x = y; +y = x; diff --git a/tests/cases/conformance/types/intersection/intersectionTypeEquivalence.ts b/tests/cases/conformance/types/intersection/intersectionTypeEquivalence.ts new file mode 100644 index 0000000000..fce399eff9 --- /dev/null +++ b/tests/cases/conformance/types/intersection/intersectionTypeEquivalence.ts @@ -0,0 +1,16 @@ +interface A { a: string } +interface B { b: string } +interface C { c: string } + +// A & B is equivalent to B & A. +var y: A & B; +var y : B & A; + +// AB & C is equivalent to A & BC, where AB is A & B and BC is B & C. +var z : A & B & C; +var z : (A & B) & C; +var z : A & (B & C); +var ab : A & B; +var bc : B & C; +var z1: typeof ab & C; +var z1: A & typeof bc; diff --git a/tests/cases/conformance/types/intersection/intersectionTypeInference.ts b/tests/cases/conformance/types/intersection/intersectionTypeInference.ts new file mode 100644 index 0000000000..32a419ff03 --- /dev/null +++ b/tests/cases/conformance/types/intersection/intersectionTypeInference.ts @@ -0,0 +1,27 @@ +function extend(obj1: T, obj2: U): T & U { + var result: T & U; + obj1 = result; + obj2 = result; + result = obj1; // Error + result = obj2; // Error + return result; +} + +var x = extend({ a: "hello" }, { b: 42 }); +var s = x.a; +var n = x.b; + +interface A { + a: T; +} + +interface B { + b: U; +} + +function foo(obj: A & B): T | U { + return undefined; +} + +var z = foo({ a: "hello", b: 42 }); +var z: string | number; diff --git a/tests/cases/conformance/types/intersection/intersectionTypeMembers.ts b/tests/cases/conformance/types/intersection/intersectionTypeMembers.ts new file mode 100644 index 0000000000..a8c92647f4 --- /dev/null +++ b/tests/cases/conformance/types/intersection/intersectionTypeMembers.ts @@ -0,0 +1,27 @@ +// An intersection type has those members that are present in any of its constituent types, +// with types that are intersections of the respective members in the constituent types + +interface A { a: string } +interface B { b: string } +interface C { c: string } + +var abc: A & B & C; +abc.a = "hello"; +abc.b = "hello"; +abc.c = "hello"; + +interface X { x: A } +interface Y { x: B } +interface Z { x: C } + +var xyz: X & Y & Z; +xyz.x.a = "hello"; +xyz.x.b = "hello"; +xyz.x.c = "hello"; + +type F1 = (x: string) => string; +type F2 = (x: number) => number; + +var f: F1 & F2; +var s = f("hello"); +var n = f(42); diff --git a/tests/cases/conformance/types/intersection/intersectionTypeOverloading.ts b/tests/cases/conformance/types/intersection/intersectionTypeOverloading.ts new file mode 100644 index 0000000000..0a0d30d8db --- /dev/null +++ b/tests/cases/conformance/types/intersection/intersectionTypeOverloading.ts @@ -0,0 +1,14 @@ +// Check that order is preserved in intersection types for purposes of +// overload resolution + +type F = (s: string) => string; +type G = (x: any) => any; + +var fg: F & G; +var gf: G & F; + +var x = fg("abc"); +var x: string; + +var y = gf("abc"); +var y: any; diff --git a/tests/cases/conformance/types/intersection/recursiveIntersectionTypes.ts b/tests/cases/conformance/types/intersection/recursiveIntersectionTypes.ts new file mode 100644 index 0000000000..d741d30ac1 --- /dev/null +++ b/tests/cases/conformance/types/intersection/recursiveIntersectionTypes.ts @@ -0,0 +1,19 @@ +type LinkedList = T & { next: LinkedList }; + +interface Entity { + name: string; +} + +interface Product extends Entity { + price: number; +} + +var entityList: LinkedList; +var s = entityList.name; +var s = entityList.next.name; +var s = entityList.next.next.name; +var s = entityList.next.next.next.name; + +var productList: LinkedList; +entityList = productList; +productList = entityList; // Error