From 2526ca563ab2c412b3c5d5c06cd0035e851dae1f Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Tue, 22 Oct 2019 09:35:08 -0700 Subject: [PATCH 1/3] Common props check:Fix intersection-parent check --- src/compiler/checker.ts | 98 +++++++++++-------- .../commonTypeIntersection.errors.txt | 22 +++++ .../reference/commonTypeIntersection.js | 11 +++ .../reference/commonTypeIntersection.symbols | 21 ++++ .../reference/commonTypeIntersection.types | 21 ++++ .../intersection/commonTypeIntersection.ts | 5 + 6 files changed, 135 insertions(+), 43 deletions(-) create mode 100644 tests/baselines/reference/commonTypeIntersection.errors.txt create mode 100644 tests/baselines/reference/commonTypeIntersection.js create mode 100644 tests/baselines/reference/commonTypeIntersection.symbols create mode 100644 tests/baselines/reference/commonTypeIntersection.types create mode 100644 tests/cases/conformance/types/intersection/commonTypeIntersection.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 647a5e8f57b48..0c14b775a2d71 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14476,7 +14476,7 @@ namespace ts { * * Ternary.Maybe if they are related with assumptions of other relationships, or * * Ternary.False if they are not related. */ - function isRelatedTo(source: Type, target: Type, reportErrors = false, headMessage?: DiagnosticMessage, isApparentIntersectionConstituent?: boolean): Ternary { + function isRelatedTo(source: Type, target: Type, reportErrors = false, headMessage?: DiagnosticMessage, isApparentIntersectionConstituent?: { skipped: boolean }): Ternary { if (isFreshLiteralType(source)) { source = (source).regularType; } @@ -14524,34 +14524,44 @@ namespace ts { isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True; const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes); - const isPerformingExcessPropertyChecks = !isApparentIntersectionConstituent && (isObjectLiteralType(source) && getObjectFlags(source) & ObjectFlags.FreshLiteral); + const isPerformingExcessPropertyChecks = isObjectLiteralType(source) && !!(getObjectFlags(source) & ObjectFlags.FreshLiteral); if (isPerformingExcessPropertyChecks) { - const discriminantType = target.flags & TypeFlags.Union ? findMatchingDiscriminantType(source, target as UnionType) : undefined; - if (hasExcessProperties(source, target, discriminantType, reportErrors)) { - if (reportErrors) { - reportRelationError(headMessage, source, target); + if (isApparentIntersectionConstituent) { + isApparentIntersectionConstituent.skipped = true; + } + else { + const discriminantType = target.flags & TypeFlags.Union ? findMatchingDiscriminantType(source, target as UnionType) : undefined; + if (hasExcessProperties(source, target, discriminantType, reportErrors)) { + if (reportErrors) { + reportRelationError(headMessage, source, target); + } + return Ternary.False; } - return Ternary.False; } } - const isPerformingCommonPropertyChecks = relation !== comparableRelation && !isApparentIntersectionConstituent && + const isPerformingCommonPropertyChecks = relation !== comparableRelation && source.flags & (TypeFlags.Primitive | TypeFlags.Object | TypeFlags.Intersection) && source !== globalObjectType && target.flags & (TypeFlags.Object | TypeFlags.Intersection) && isWeakType(target) && (getPropertiesOfType(source).length > 0 || typeHasCallOrConstructSignatures(source)); - if (isPerformingCommonPropertyChecks && !hasCommonProperties(source, target, isComparingJsxAttributes)) { - if (reportErrors) { - const calls = getSignaturesOfType(source, SignatureKind.Call); - const constructs = getSignaturesOfType(source, SignatureKind.Construct); - if (calls.length > 0 && isRelatedTo(getReturnTypeOfSignature(calls[0]), target, /*reportErrors*/ false) || - constructs.length > 0 && isRelatedTo(getReturnTypeOfSignature(constructs[0]), target, /*reportErrors*/ false)) { - reportError(Diagnostics.Value_of_type_0_has_no_properties_in_common_with_type_1_Did_you_mean_to_call_it, typeToString(source), typeToString(target)); - } - else { - reportError(Diagnostics.Type_0_has_no_properties_in_common_with_type_1, typeToString(source), typeToString(target)); + if (isPerformingCommonPropertyChecks) { + if (isApparentIntersectionConstituent) { + isApparentIntersectionConstituent.skipped = true; + } + else if(!hasCommonProperties(source, target, isComparingJsxAttributes)) { + if (reportErrors) { + const calls = getSignaturesOfType(source, SignatureKind.Call); + const constructs = getSignaturesOfType(source, SignatureKind.Construct); + if (calls.length > 0 && isRelatedTo(getReturnTypeOfSignature(calls[0]), target, /*reportErrors*/ false) || + constructs.length > 0 && isRelatedTo(getReturnTypeOfSignature(constructs[0]), target, /*reportErrors*/ false)) { + reportError(Diagnostics.Value_of_type_0_has_no_properties_in_common_with_type_1_Did_you_mean_to_call_it, typeToString(source), typeToString(target)); + } + else { + reportError(Diagnostics.Type_0_has_no_properties_in_common_with_type_1, typeToString(source), typeToString(target)); + } } + return Ternary.False; } - return Ternary.False; } let result = Ternary.False; @@ -14563,26 +14573,27 @@ namespace ts { // and we need to handle "each" relations before "some" relations for the same kind of type. if (source.flags & TypeFlags.Union) { result = relation === comparableRelation ? - someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), isIntersectionConstituent) : + someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), isIntersectionConstituent ? { skipped: false } : undefined) : eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive)); } else { if (target.flags & TypeFlags.Union) { result = typeRelatedToSomeType(getRegularTypeOfObjectLiteral(source), target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive)); - if (result && (isPerformingExcessPropertyChecks || isPerformingCommonPropertyChecks)) { + if (result && (isPerformingExcessPropertyChecks || isPerformingCommonPropertyChecks) && !isApparentIntersectionConstituent) { // Validate against excess props using the original `source` const discriminantType = findMatchingDiscriminantType(source, target as UnionType) || filterPrimitivesIfContainsNonPrimitive(target as UnionType); - if (!propertiesRelatedTo(source, discriminantType, reportErrors, /*excludedProperties*/ undefined, isIntersectionConstituent)) { + if (!propertiesRelatedTo(source, discriminantType, reportErrors, /*excludedProperties*/ undefined, isIntersectionConstituent ? { skipped: false } : undefined)) { return Ternary.False; } } } else if (target.flags & TypeFlags.Intersection) { isIntersectionConstituent = true; // set here to affect the following trio of checks - result = typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors); - if (result && (isPerformingExcessPropertyChecks || isPerformingCommonPropertyChecks)) { + const resres = { skipped: false }; + result = typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors, resres); + if (result && (isPerformingExcessPropertyChecks || resres.skipped)) { // Validate against excess props using the original `source` - if (!propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, /*isIntersectionConstituent*/ false)) { + if (!propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, /*isIntersectionConstituent*/ undefined)) { return Ternary.False; } } @@ -14601,10 +14612,10 @@ namespace ts { // // - For a primitive type or type parameter (such as 'number = A & B') there is no point in // breaking the intersection apart. - result = someTypeRelatedToType(source, target, /*reportErrors*/ false, /*isIntersectionConstituent*/ true); + result = someTypeRelatedToType(source, target, /*reportErrors*/ false, /*isIntersectionConstituent*/ { skipped: false }); } if (!result && (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable)) { - if (result = recursiveTypeRelatedTo(source, target, reportErrors, isIntersectionConstituent)) { + if (result = recursiveTypeRelatedTo(source, target, reportErrors, isIntersectionConstituent ? { skipped: false } : undefined)) { resetErrorInfo(saveErrorInfo); } } @@ -14627,7 +14638,7 @@ namespace ts { if (constraint && (source.flags & TypeFlags.Intersection || target.flags & TypeFlags.Union)) { if (everyType(constraint, c => c !== source)) { // Skip comparison if expansion contains the source itself // TODO: Stack errors so we get a pyramid for the "normal" comparison above, _and_ a second for this - if (result = isRelatedTo(constraint, target, /*reportErrors*/ false, /*headMessage*/ undefined, isIntersectionConstituent)) { + if (result = isRelatedTo(constraint, target, /*reportErrors*/ false, /*headMessage*/ undefined, isIntersectionConstituent ? { skipped: false } : undefined)) { resetErrorInfo(saveErrorInfo); } } @@ -14676,7 +14687,7 @@ namespace ts { let result: Ternary; const flags = source.flags & target.flags; if (flags & TypeFlags.Object || flags & TypeFlags.IndexedAccess || flags & TypeFlags.Conditional || flags & TypeFlags.Index || flags & TypeFlags.Substitution) { - return recursiveTypeRelatedTo(source, target, /*reportErrors*/ false, /*isIntersectionConstituent*/ false); + return recursiveTypeRelatedTo(source, target, /*reportErrors*/ false, /*isIntersectionConstituent*/ undefined); } if (flags & (TypeFlags.Union | TypeFlags.Intersection)) { if (result = eachTypeRelatedToSomeType(source, target)) { @@ -14882,11 +14893,11 @@ namespace ts { return undefined; } - function typeRelatedToEachType(source: Type, target: IntersectionType, reportErrors: boolean): Ternary { + function typeRelatedToEachType(source: Type, target: IntersectionType, reportErrors: boolean, box: { skipped: boolean }): Ternary { let result = Ternary.True; const targetTypes = target.types; for (const targetType of targetTypes) { - const related = isRelatedTo(source, targetType, reportErrors, /*headMessage*/ undefined, /*isIntersectionConstituent*/ true); + const related = isRelatedTo(source, targetType, reportErrors, /*headMessage*/ undefined, /*isIntersectionConstituent*/ box); if (!related) { return Ternary.False; } @@ -14895,7 +14906,7 @@ namespace ts { return result; } - function someTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary { + function someTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean, isIntersectionConstituent: { skipped: boolean } | undefined): Ternary { const sourceTypes = source.types; if (source.flags & TypeFlags.Union && containsType(sourceTypes, target)) { return Ternary.True; @@ -14923,7 +14934,7 @@ namespace ts { return result; } - function typeArgumentsRelatedTo(sources: readonly Type[] = emptyArray, targets: readonly Type[] = emptyArray, variances: readonly VarianceFlags[] = emptyArray, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary { + function typeArgumentsRelatedTo(sources: readonly Type[] = emptyArray, targets: readonly Type[] = emptyArray, variances: readonly VarianceFlags[] = emptyArray, reportErrors: boolean, isIntersectionConstituent: { skipped: boolean } | undefined): Ternary { if (sources.length !== targets.length && relation === identityRelation) { return Ternary.False; } @@ -14985,7 +14996,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 recursiveTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary { + function recursiveTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, isIntersectionConstituent: { skipped: boolean } | undefined): Ternary { if (overflow) { return Ternary.False; } @@ -15069,7 +15080,7 @@ namespace ts { return result; } - function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary { + function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, isIntersectionConstituent: { skipped: boolean } | undefined): Ternary { const flags = source.flags & target.flags; if (relation === identityRelation && !(flags & TypeFlags.Object)) { if (flags & TypeFlags.Index) { @@ -15374,7 +15385,7 @@ namespace ts { } return Ternary.False; - function relateVariances(sourceTypeArguments: readonly Type[] | undefined, targetTypeArguments: readonly Type[] | undefined, variances: VarianceFlags[], isIntersectionConstituent: boolean) { + function relateVariances(sourceTypeArguments: readonly Type[] | undefined, targetTypeArguments: readonly Type[] | undefined, variances: VarianceFlags[], isIntersectionConstituent: { skipped: boolean } | undefined) { if (result = typeArgumentsRelatedTo(sourceTypeArguments, targetTypeArguments, variances, reportErrors, isIntersectionConstituent)) { return result; } @@ -15503,7 +15514,7 @@ namespace ts { if (!targetProperty) continue outer; if (sourceProperty === targetProperty) continue; // We compare the source property to the target in the context of a single discriminant type. - const related = propertyRelatedTo(source, target, sourceProperty, targetProperty, _ => combination[i], /*reportErrors*/ false, /*isIntersectionConstituent*/ false); + const related = propertyRelatedTo(source, target, sourceProperty, targetProperty, _ => combination[i], /*reportErrors*/ false, /*isIntersectionConstituent*/ undefined); // If the target property could not be found, or if the properties were not related, // then this constituent is not a match. if (!related) { @@ -15522,7 +15533,7 @@ namespace ts { // Compare the remaining non-discriminant properties of each match. let result = Ternary.True; for (const type of matchingTypes) { - result &= propertiesRelatedTo(source, type, /*reportErrors*/ false, excludedProperties, /*isIntersectionConstituent*/ false); + result &= propertiesRelatedTo(source, type, /*reportErrors*/ false, excludedProperties, /*isIntersectionConstituent*/ undefined); if (result) { result &= signaturesRelatedTo(source, type, SignatureKind.Call, /*reportStructuralErrors*/ false); if (result) { @@ -15558,7 +15569,7 @@ namespace ts { return result || properties; } - function isPropertySymbolTypeRelated(sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary { + function isPropertySymbolTypeRelated(sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean, isIntersectionConstituent: { skipped: boolean } | undefined): Ternary { const targetIsOptional = strictNullChecks && !!(getCheckFlags(targetProp) & CheckFlags.Partial); const source = getTypeOfSourceProperty(sourceProp); if (getCheckFlags(targetProp) & CheckFlags.DeferredType && !getSymbolLinks(targetProp).type) { @@ -15570,7 +15581,7 @@ namespace ts { let result = unionParent ? Ternary.False : Ternary.True; const targetTypes = links.deferralConstituents!; for (const targetType of targetTypes) { - const related = isRelatedTo(source, targetType, /*reportErrors*/ false, /*headMessage*/ undefined, /*isIntersectionConstituent*/ !unionParent); + const related = isRelatedTo(source, targetType, /*reportErrors*/ false, /*headMessage*/ undefined, /*isIntersectionConstituent*/ unionParent ? undefined : { skipped: false }); if (!unionParent) { if (!related) { // Can't assign to a target individually - have to fallback to assigning to the _whole_ intersection (which forces normalization) @@ -15601,7 +15612,7 @@ namespace ts { } } - function propertyRelatedTo(source: Type, target: Type, sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary { + function propertyRelatedTo(source: Type, target: Type, sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean, isIntersectionConstituent: { skipped: boolean } | undefined): Ternary { const sourcePropFlags = getDeclarationModifierFlagsFromSymbol(sourceProp); const targetPropFlags = getDeclarationModifierFlagsFromSymbol(targetProp); if (sourcePropFlags & ModifierFlags.Private || targetPropFlags & ModifierFlags.Private) { @@ -15668,7 +15679,7 @@ namespace ts { return related; } - function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean, excludedProperties: UnderscoreEscapedMap | undefined, isIntersectionConstituent: boolean): Ternary { + function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean, excludedProperties: UnderscoreEscapedMap | undefined, isIntersectionConstituent: { skipped: boolean } | undefined): Ternary { if (relation === identityRelation) { return propertiesIdenticalTo(source, target, excludedProperties); } @@ -16055,6 +16066,7 @@ namespace ts { /** * A type is 'weak' if it is an object type with at least one optional property * and no required properties, call/construct signatures or index signatures + * Note: current change be further fixed by distinguishing "very weak" -- object or intersectoin w/some very weak -- from "weak" -- object or intersection w/all */ function isWeakType(type: Type): boolean { if (type.flags & TypeFlags.Object) { @@ -16065,7 +16077,7 @@ namespace ts { every(resolved.properties, p => !!(p.flags & SymbolFlags.Optional)); } if (type.flags & TypeFlags.Intersection) { - return every((type).types, isWeakType); + return every((type as IntersectionType).types, isWeakType); } return false; } diff --git a/tests/baselines/reference/commonTypeIntersection.errors.txt b/tests/baselines/reference/commonTypeIntersection.errors.txt new file mode 100644 index 0000000000000..44580132d5914 --- /dev/null +++ b/tests/baselines/reference/commonTypeIntersection.errors.txt @@ -0,0 +1,22 @@ +tests/cases/conformance/types/intersection/commonTypeIntersection.ts(2,5): error TS2326: Types of property '__typename' are incompatible. + Type '"TypeTwo" | undefined' is not assignable to type '"TypeOne" | undefined'. + Type '"TypeTwo"' is not assignable to type '"TypeOne" | undefined'. +tests/cases/conformance/types/intersection/commonTypeIntersection.ts(4,5): error TS2326: Types of property '__typename' are incompatible. + Type '"TypeTwo" | undefined' is not assignable to type '"TypeOne" | undefined'. + Type '"TypeTwo"' is not assignable to type '"TypeOne" | undefined'. + + +==== tests/cases/conformance/types/intersection/commonTypeIntersection.ts (2 errors) ==== + declare let x1: { __typename?: 'TypeTwo' } & { a: boolean }; + let y1: { __typename?: 'TypeOne' } & { a: boolean} = x1; // No error! + ~~ +!!! error TS2326: Types of property '__typename' are incompatible. +!!! error TS2326: Type '"TypeTwo" | undefined' is not assignable to type '"TypeOne" | undefined'. +!!! error TS2326: Type '"TypeTwo"' is not assignable to type '"TypeOne" | undefined'. + declare let x2: { __typename?: 'TypeTwo' } & string; + let y2: { __typename?: 'TypeOne' } & string = x2; // No error! + ~~ +!!! error TS2326: Types of property '__typename' are incompatible. +!!! error TS2326: Type '"TypeTwo" | undefined' is not assignable to type '"TypeOne" | undefined'. +!!! error TS2326: Type '"TypeTwo"' is not assignable to type '"TypeOne" | undefined'. + \ No newline at end of file diff --git a/tests/baselines/reference/commonTypeIntersection.js b/tests/baselines/reference/commonTypeIntersection.js new file mode 100644 index 0000000000000..98ec8957df4f3 --- /dev/null +++ b/tests/baselines/reference/commonTypeIntersection.js @@ -0,0 +1,11 @@ +//// [commonTypeIntersection.ts] +declare let x1: { __typename?: 'TypeTwo' } & { a: boolean }; +let y1: { __typename?: 'TypeOne' } & { a: boolean} = x1; // No error! +declare let x2: { __typename?: 'TypeTwo' } & string; +let y2: { __typename?: 'TypeOne' } & string = x2; // No error! + + +//// [commonTypeIntersection.js] +"use strict"; +var y1 = x1; // No error! +var y2 = x2; // No error! diff --git a/tests/baselines/reference/commonTypeIntersection.symbols b/tests/baselines/reference/commonTypeIntersection.symbols new file mode 100644 index 0000000000000..5753657bf62ed --- /dev/null +++ b/tests/baselines/reference/commonTypeIntersection.symbols @@ -0,0 +1,21 @@ +=== tests/cases/conformance/types/intersection/commonTypeIntersection.ts === +declare let x1: { __typename?: 'TypeTwo' } & { a: boolean }; +>x1 : Symbol(x1, Decl(commonTypeIntersection.ts, 0, 11)) +>__typename : Symbol(__typename, Decl(commonTypeIntersection.ts, 0, 17)) +>a : Symbol(a, Decl(commonTypeIntersection.ts, 0, 46)) + +let y1: { __typename?: 'TypeOne' } & { a: boolean} = x1; // No error! +>y1 : Symbol(y1, Decl(commonTypeIntersection.ts, 1, 3)) +>__typename : Symbol(__typename, Decl(commonTypeIntersection.ts, 1, 9)) +>a : Symbol(a, Decl(commonTypeIntersection.ts, 1, 38)) +>x1 : Symbol(x1, Decl(commonTypeIntersection.ts, 0, 11)) + +declare let x2: { __typename?: 'TypeTwo' } & string; +>x2 : Symbol(x2, Decl(commonTypeIntersection.ts, 2, 11)) +>__typename : Symbol(__typename, Decl(commonTypeIntersection.ts, 2, 17)) + +let y2: { __typename?: 'TypeOne' } & string = x2; // No error! +>y2 : Symbol(y2, Decl(commonTypeIntersection.ts, 3, 3)) +>__typename : Symbol(__typename, Decl(commonTypeIntersection.ts, 3, 9)) +>x2 : Symbol(x2, Decl(commonTypeIntersection.ts, 2, 11)) + diff --git a/tests/baselines/reference/commonTypeIntersection.types b/tests/baselines/reference/commonTypeIntersection.types new file mode 100644 index 0000000000000..c7fb3e9fdfd35 --- /dev/null +++ b/tests/baselines/reference/commonTypeIntersection.types @@ -0,0 +1,21 @@ +=== tests/cases/conformance/types/intersection/commonTypeIntersection.ts === +declare let x1: { __typename?: 'TypeTwo' } & { a: boolean }; +>x1 : { __typename?: "TypeTwo" | undefined; } & { a: boolean; } +>__typename : "TypeTwo" | undefined +>a : boolean + +let y1: { __typename?: 'TypeOne' } & { a: boolean} = x1; // No error! +>y1 : { __typename?: "TypeOne" | undefined; } & { a: boolean; } +>__typename : "TypeOne" | undefined +>a : boolean +>x1 : { __typename?: "TypeTwo" | undefined; } & { a: boolean; } + +declare let x2: { __typename?: 'TypeTwo' } & string; +>x2 : { __typename?: "TypeTwo" | undefined; } & string +>__typename : "TypeTwo" | undefined + +let y2: { __typename?: 'TypeOne' } & string = x2; // No error! +>y2 : { __typename?: "TypeOne" | undefined; } & string +>__typename : "TypeOne" | undefined +>x2 : { __typename?: "TypeTwo" | undefined; } & string + diff --git a/tests/cases/conformance/types/intersection/commonTypeIntersection.ts b/tests/cases/conformance/types/intersection/commonTypeIntersection.ts new file mode 100644 index 0000000000000..2409b5139a988 --- /dev/null +++ b/tests/cases/conformance/types/intersection/commonTypeIntersection.ts @@ -0,0 +1,5 @@ +// @strict: true +declare let x1: { __typename?: 'TypeTwo' } & { a: boolean }; +let y1: { __typename?: 'TypeOne' } & { a: boolean} = x1; // No error! +declare let x2: { __typename?: 'TypeTwo' } & string; +let y2: { __typename?: 'TypeOne' } & string = x2; // No error! From 7b5c0850e288203caee802dcb3e09a92a9333fe8 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Tue, 22 Oct 2019 09:48:02 -0700 Subject: [PATCH 2/3] fix trailing space lint --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0c14b775a2d71..0015bd9453ec3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16066,7 +16066,7 @@ namespace ts { /** * A type is 'weak' if it is an object type with at least one optional property * and no required properties, call/construct signatures or index signatures - * Note: current change be further fixed by distinguishing "very weak" -- object or intersectoin w/some very weak -- from "weak" -- object or intersection w/all + * Note: current change be further fixed by distinguishing "very weak" -- object or intersectoin w/some very weak -- from "weak" -- object or intersection w/all */ function isWeakType(type: Type): boolean { if (type.flags & TypeFlags.Object) { From 9ba344cc58144dd16aaf785be568db6943e82500 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Tue, 22 Oct 2019 13:38:04 -0700 Subject: [PATCH 3/3] Update baselines --- ...actReadonlyHOCAssignabilityReal.errors.txt | 20 +++++++++++++++++++ .../reactReadonlyHOCAssignabilityReal.types | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/reactReadonlyHOCAssignabilityReal.errors.txt diff --git a/tests/baselines/reference/reactReadonlyHOCAssignabilityReal.errors.txt b/tests/baselines/reference/reactReadonlyHOCAssignabilityReal.errors.txt new file mode 100644 index 0000000000000..efa2118d28302 --- /dev/null +++ b/tests/baselines/reference/reactReadonlyHOCAssignabilityReal.errors.txt @@ -0,0 +1,20 @@ +tests/cases/compiler/reactReadonlyHOCAssignabilityReal.tsx(7,21): error TS2326: Types of property 'name' are incompatible. + Type '"Matt"' is not assignable to type 'P["name"] & string'. + Type '"Matt"' is not assignable to type 'P["name"]'. + + +==== tests/cases/compiler/reactReadonlyHOCAssignabilityReal.tsx (1 errors) ==== + /// + import * as React from "react"; + + function myHigherOrderComponent

(Inner: React.ComponentClass

): React.ComponentClass

{ + return class OuterComponent extends React.Component

{ + render() { + return ; + ~~~~~ +!!! error TS2326: Types of property 'name' are incompatible. +!!! error TS2326: Type '"Matt"' is not assignable to type 'P["name"] & string'. +!!! error TS2326: Type '"Matt"' is not assignable to type 'P["name"]'. + } + }; + } \ No newline at end of file diff --git a/tests/baselines/reference/reactReadonlyHOCAssignabilityReal.types b/tests/baselines/reference/reactReadonlyHOCAssignabilityReal.types index 1f67ab5adf936..29d46f7d8b277 100644 --- a/tests/baselines/reference/reactReadonlyHOCAssignabilityReal.types +++ b/tests/baselines/reference/reactReadonlyHOCAssignabilityReal.types @@ -26,7 +26,7 @@ function myHigherOrderComponent

(Inner: React.ComponentClass

this.props : Readonly<{ children?: React.ReactNode; }> & Readonly

>this : this >props : Readonly<{ children?: React.ReactNode; }> & Readonly

->name : "Matt" +>name : string } }; }