diff --git a/lib/tsc.js b/lib/tsc.js index 010f5d28f9c..a0951afd1fa 100644 --- a/lib/tsc.js +++ b/lib/tsc.js @@ -41603,7 +41603,7 @@ var ts; return "continue-outer"; if (sourceProperty === targetProperty) return "continue"; - var related = propertyRelatedTo(source, target, sourceProperty, targetProperty, function (_) { return combination[i]; }, false, 0); + var related = propertyRelatedTo(source, target, sourceProperty, targetProperty, function (_) { return combination[i]; }, false, 0, strictNullChecks || relation === comparableRelation); if (!related) { return "continue-outer"; } @@ -41702,7 +41702,7 @@ var ts; return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors, undefined, intersectionState); } } - function propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, intersectionState) { + function propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, intersectionState, skipOptional) { var sourcePropFlags = ts.getDeclarationModifierFlagsFromSymbol(sourceProp); var targetPropFlags = ts.getDeclarationModifierFlagsFromSymbol(targetProp); if (sourcePropFlags & 8 || targetPropFlags & 8) { @@ -41739,7 +41739,7 @@ var ts; } return 0; } - if (relation !== comparableRelation && sourceProp.flags & 16777216 && !(targetProp.flags & 16777216)) { + if (!skipOptional && sourceProp.flags & 16777216 && !(targetProp.flags & 16777216)) { if (reportErrors) { reportError(ts.Diagnostics.Property_0_is_optional_in_type_1_but_required_in_type_2, symbolToString(targetProp), typeToString(source), typeToString(target)); } @@ -41853,7 +41853,7 @@ var ts; if (!(targetProp.flags & 4194304) && (!numericNamesOnly || isNumericLiteralName(name) || name === "length")) { var sourceProp = getPropertyOfType(source, name); if (sourceProp && sourceProp !== targetProp) { - var related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors, intersectionState); + var related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors, intersectionState, relation === comparableRelation); if (!related) { return 0; } diff --git a/lib/tsserver.js b/lib/tsserver.js index bbfa3e787cf..9815970212d 100644 --- a/lib/tsserver.js +++ b/lib/tsserver.js @@ -49915,7 +49915,7 @@ var ts; if (sourceProperty === targetProperty) return "continue"; // We compare the source property to the target in the context of a single discriminant type. - var related = propertyRelatedTo(source, target, sourceProperty, targetProperty, function (_) { return combination[i]; }, /*reportErrors*/ false, 0 /* None */); + var related = propertyRelatedTo(source, target, sourceProperty, targetProperty, function (_) { return combination[i]; }, /*reportErrors*/ false, 0 /* None */, /*skipOptional*/ strictNullChecks || relation === comparableRelation); // If the target property could not be found, or if the properties were not related, // then this constituent is not a match. if (!related) { @@ -50023,7 +50023,7 @@ var ts; return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors, /*headMessage*/ undefined, intersectionState); } } - function propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, intersectionState) { + function propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, intersectionState, skipOptional) { var sourcePropFlags = ts.getDeclarationModifierFlagsFromSymbol(sourceProp); var targetPropFlags = ts.getDeclarationModifierFlagsFromSymbol(targetProp); if (sourcePropFlags & 8 /* Private */ || targetPropFlags & 8 /* Private */) { @@ -50062,7 +50062,7 @@ var ts; return 0 /* False */; } // When checking for comparability, be more lenient with optional properties. - if (relation !== comparableRelation && sourceProp.flags & 16777216 /* Optional */ && !(targetProp.flags & 16777216 /* Optional */)) { + if (!skipOptional && sourceProp.flags & 16777216 /* Optional */ && !(targetProp.flags & 16777216 /* Optional */)) { // TypeScript 1.0 spec (April 2014): 3.8.3 // S is a subtype of a type T, and T is a supertype of S if ... // S' and T are object types and, for each member M in T.. @@ -50187,7 +50187,7 @@ var ts; if (!(targetProp.flags & 4194304 /* Prototype */) && (!numericNamesOnly || isNumericLiteralName(name) || name === "length")) { var sourceProp = getPropertyOfType(source, name); if (sourceProp && sourceProp !== targetProp) { - var related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors, intersectionState); + var related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors, intersectionState, relation === comparableRelation); if (!related) { return 0 /* False */; } diff --git a/lib/tsserverlibrary.js b/lib/tsserverlibrary.js index 6a69a6e8a20..b39b619c55c 100644 --- a/lib/tsserverlibrary.js +++ b/lib/tsserverlibrary.js @@ -50065,7 +50065,7 @@ var ts; if (sourceProperty === targetProperty) return "continue"; // We compare the source property to the target in the context of a single discriminant type. - var related = propertyRelatedTo(source, target, sourceProperty, targetProperty, function (_) { return combination[i]; }, /*reportErrors*/ false, 0 /* None */); + var related = propertyRelatedTo(source, target, sourceProperty, targetProperty, function (_) { return combination[i]; }, /*reportErrors*/ false, 0 /* None */, /*skipOptional*/ strictNullChecks || relation === comparableRelation); // If the target property could not be found, or if the properties were not related, // then this constituent is not a match. if (!related) { @@ -50173,7 +50173,7 @@ var ts; return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors, /*headMessage*/ undefined, intersectionState); } } - function propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, intersectionState) { + function propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, intersectionState, skipOptional) { var sourcePropFlags = ts.getDeclarationModifierFlagsFromSymbol(sourceProp); var targetPropFlags = ts.getDeclarationModifierFlagsFromSymbol(targetProp); if (sourcePropFlags & 8 /* Private */ || targetPropFlags & 8 /* Private */) { @@ -50212,7 +50212,7 @@ var ts; return 0 /* False */; } // When checking for comparability, be more lenient with optional properties. - if (relation !== comparableRelation && sourceProp.flags & 16777216 /* Optional */ && !(targetProp.flags & 16777216 /* Optional */)) { + if (!skipOptional && sourceProp.flags & 16777216 /* Optional */ && !(targetProp.flags & 16777216 /* Optional */)) { // TypeScript 1.0 spec (April 2014): 3.8.3 // S is a subtype of a type T, and T is a supertype of S if ... // S' and T are object types and, for each member M in T.. @@ -50337,7 +50337,7 @@ var ts; if (!(targetProp.flags & 4194304 /* Prototype */) && (!numericNamesOnly || isNumericLiteralName(name) || name === "length")) { var sourceProp = getPropertyOfType(source, name); if (sourceProp && sourceProp !== targetProp) { - var related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors, intersectionState); + var related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors, intersectionState, relation === comparableRelation); if (!related) { return 0 /* False */; } diff --git a/lib/typescript.js b/lib/typescript.js index ff8f7841af7..78cc5c394cb 100644 --- a/lib/typescript.js +++ b/lib/typescript.js @@ -50065,7 +50065,7 @@ var ts; if (sourceProperty === targetProperty) return "continue"; // We compare the source property to the target in the context of a single discriminant type. - var related = propertyRelatedTo(source, target, sourceProperty, targetProperty, function (_) { return combination[i]; }, /*reportErrors*/ false, 0 /* None */); + var related = propertyRelatedTo(source, target, sourceProperty, targetProperty, function (_) { return combination[i]; }, /*reportErrors*/ false, 0 /* None */, /*skipOptional*/ strictNullChecks || relation === comparableRelation); // If the target property could not be found, or if the properties were not related, // then this constituent is not a match. if (!related) { @@ -50173,7 +50173,7 @@ var ts; return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors, /*headMessage*/ undefined, intersectionState); } } - function propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, intersectionState) { + function propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, intersectionState, skipOptional) { var sourcePropFlags = ts.getDeclarationModifierFlagsFromSymbol(sourceProp); var targetPropFlags = ts.getDeclarationModifierFlagsFromSymbol(targetProp); if (sourcePropFlags & 8 /* Private */ || targetPropFlags & 8 /* Private */) { @@ -50212,7 +50212,7 @@ var ts; return 0 /* False */; } // When checking for comparability, be more lenient with optional properties. - if (relation !== comparableRelation && sourceProp.flags & 16777216 /* Optional */ && !(targetProp.flags & 16777216 /* Optional */)) { + if (!skipOptional && sourceProp.flags & 16777216 /* Optional */ && !(targetProp.flags & 16777216 /* Optional */)) { // TypeScript 1.0 spec (April 2014): 3.8.3 // S is a subtype of a type T, and T is a supertype of S if ... // S' and T are object types and, for each member M in T.. @@ -50337,7 +50337,7 @@ var ts; if (!(targetProp.flags & 4194304 /* Prototype */) && (!numericNamesOnly || isNumericLiteralName(name) || name === "length")) { var sourceProp = getPropertyOfType(source, name); if (sourceProp && sourceProp !== targetProp) { - var related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors, intersectionState); + var related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors, intersectionState, relation === comparableRelation); if (!related) { return 0 /* False */; } diff --git a/lib/typescriptServices.js b/lib/typescriptServices.js index d73da5100e8..d466177408b 100644 --- a/lib/typescriptServices.js +++ b/lib/typescriptServices.js @@ -50065,7 +50065,7 @@ var ts; if (sourceProperty === targetProperty) return "continue"; // We compare the source property to the target in the context of a single discriminant type. - var related = propertyRelatedTo(source, target, sourceProperty, targetProperty, function (_) { return combination[i]; }, /*reportErrors*/ false, 0 /* None */); + var related = propertyRelatedTo(source, target, sourceProperty, targetProperty, function (_) { return combination[i]; }, /*reportErrors*/ false, 0 /* None */, /*skipOptional*/ strictNullChecks || relation === comparableRelation); // If the target property could not be found, or if the properties were not related, // then this constituent is not a match. if (!related) { @@ -50173,7 +50173,7 @@ var ts; return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors, /*headMessage*/ undefined, intersectionState); } } - function propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, intersectionState) { + function propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, intersectionState, skipOptional) { var sourcePropFlags = ts.getDeclarationModifierFlagsFromSymbol(sourceProp); var targetPropFlags = ts.getDeclarationModifierFlagsFromSymbol(targetProp); if (sourcePropFlags & 8 /* Private */ || targetPropFlags & 8 /* Private */) { @@ -50212,7 +50212,7 @@ var ts; return 0 /* False */; } // When checking for comparability, be more lenient with optional properties. - if (relation !== comparableRelation && sourceProp.flags & 16777216 /* Optional */ && !(targetProp.flags & 16777216 /* Optional */)) { + if (!skipOptional && sourceProp.flags & 16777216 /* Optional */ && !(targetProp.flags & 16777216 /* Optional */)) { // TypeScript 1.0 spec (April 2014): 3.8.3 // S is a subtype of a type T, and T is a supertype of S if ... // S' and T are object types and, for each member M in T.. @@ -50337,7 +50337,7 @@ var ts; if (!(targetProp.flags & 4194304 /* Prototype */) && (!numericNamesOnly || isNumericLiteralName(name) || name === "length")) { var sourceProp = getPropertyOfType(source, name); if (sourceProp && sourceProp !== targetProp) { - var related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors, intersectionState); + var related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors, intersectionState, relation === comparableRelation); if (!related) { return 0 /* False */; } diff --git a/lib/typingsInstaller.js b/lib/typingsInstaller.js index d2593265096..84de64a8576 100644 --- a/lib/typingsInstaller.js +++ b/lib/typingsInstaller.js @@ -49904,7 +49904,7 @@ var ts; if (sourceProperty === targetProperty) return "continue"; // We compare the source property to the target in the context of a single discriminant type. - var related = propertyRelatedTo(source, target, sourceProperty, targetProperty, function (_) { return combination[i]; }, /*reportErrors*/ false, 0 /* None */); + var related = propertyRelatedTo(source, target, sourceProperty, targetProperty, function (_) { return combination[i]; }, /*reportErrors*/ false, 0 /* None */, /*skipOptional*/ strictNullChecks || relation === comparableRelation); // If the target property could not be found, or if the properties were not related, // then this constituent is not a match. if (!related) { @@ -50012,7 +50012,7 @@ var ts; return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors, /*headMessage*/ undefined, intersectionState); } } - function propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, intersectionState) { + function propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, intersectionState, skipOptional) { var sourcePropFlags = ts.getDeclarationModifierFlagsFromSymbol(sourceProp); var targetPropFlags = ts.getDeclarationModifierFlagsFromSymbol(targetProp); if (sourcePropFlags & 8 /* Private */ || targetPropFlags & 8 /* Private */) { @@ -50051,7 +50051,7 @@ var ts; return 0 /* False */; } // When checking for comparability, be more lenient with optional properties. - if (relation !== comparableRelation && sourceProp.flags & 16777216 /* Optional */ && !(targetProp.flags & 16777216 /* Optional */)) { + if (!skipOptional && sourceProp.flags & 16777216 /* Optional */ && !(targetProp.flags & 16777216 /* Optional */)) { // TypeScript 1.0 spec (April 2014): 3.8.3 // S is a subtype of a type T, and T is a supertype of S if ... // S' and T are object types and, for each member M in T.. @@ -50176,7 +50176,7 @@ var ts; if (!(targetProp.flags & 4194304 /* Prototype */) && (!numericNamesOnly || isNumericLiteralName(name) || name === "length")) { var sourceProp = getPropertyOfType(source, name); if (sourceProp && sourceProp !== targetProp) { - var related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors, intersectionState); + var related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors, intersectionState, relation === comparableRelation); if (!related) { return 0 /* False */; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0bf27e3f07b..25e25e43757 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16639,7 +16639,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, IntersectionState.None); + const related = propertyRelatedTo(source, target, sourceProperty, targetProperty, _ => combination[i], /*reportErrors*/ false, IntersectionState.None, /*skipOptional*/ strictNullChecks || relation === comparableRelation); // If the target property could not be found, or if the properties were not related, // then this constituent is not a match. if (!related) { @@ -16737,7 +16737,7 @@ namespace ts { } } - function propertyRelatedTo(source: Type, target: Type, sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary { + function propertyRelatedTo(source: Type, target: Type, sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean, intersectionState: IntersectionState, skipOptional: boolean): Ternary { const sourcePropFlags = getDeclarationModifierFlagsFromSymbol(sourceProp); const targetPropFlags = getDeclarationModifierFlagsFromSymbol(targetProp); if (sourcePropFlags & ModifierFlags.Private || targetPropFlags & ModifierFlags.Private) { @@ -16780,7 +16780,7 @@ namespace ts { return Ternary.False; } // When checking for comparability, be more lenient with optional properties. - if (relation !== comparableRelation && sourceProp.flags & SymbolFlags.Optional && !(targetProp.flags & SymbolFlags.Optional)) { + if (!skipOptional && sourceProp.flags & SymbolFlags.Optional && !(targetProp.flags & SymbolFlags.Optional)) { // TypeScript 1.0 spec (April 2014): 3.8.3 // S is a subtype of a type T, and T is a supertype of S if ... // S' and T are object types and, for each member M in T.. @@ -16910,7 +16910,7 @@ namespace ts { if (!(targetProp.flags & SymbolFlags.Prototype) && (!numericNamesOnly || isNumericLiteralName(name) || name === "length")) { const sourceProp = getPropertyOfType(source, name); if (sourceProp && sourceProp !== targetProp) { - const related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors, intersectionState); + const related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors, intersectionState, relation === comparableRelation); if (!related) { return Ternary.False; } diff --git a/tests/baselines/reference/unionRelationshipCheckPasses.js b/tests/baselines/reference/unionRelationshipCheckPasses.js new file mode 100644 index 00000000000..65e90ed34e8 --- /dev/null +++ b/tests/baselines/reference/unionRelationshipCheckPasses.js @@ -0,0 +1,7 @@ +//// [unionRelationshipCheckPasses.ts] +const item: { foo?: undefined } | { foo: number } = null as any as { foo?: number | undefined }; + + +//// [unionRelationshipCheckPasses.js] +"use strict"; +var item = null; diff --git a/tests/baselines/reference/unionRelationshipCheckPasses.symbols b/tests/baselines/reference/unionRelationshipCheckPasses.symbols new file mode 100644 index 00000000000..3fda4578bdb --- /dev/null +++ b/tests/baselines/reference/unionRelationshipCheckPasses.symbols @@ -0,0 +1,7 @@ +=== tests/cases/compiler/unionRelationshipCheckPasses.ts === +const item: { foo?: undefined } | { foo: number } = null as any as { foo?: number | undefined }; +>item : Symbol(item, Decl(unionRelationshipCheckPasses.ts, 0, 5)) +>foo : Symbol(foo, Decl(unionRelationshipCheckPasses.ts, 0, 13)) +>foo : Symbol(foo, Decl(unionRelationshipCheckPasses.ts, 0, 35)) +>foo : Symbol(foo, Decl(unionRelationshipCheckPasses.ts, 0, 68)) + diff --git a/tests/baselines/reference/unionRelationshipCheckPasses.types b/tests/baselines/reference/unionRelationshipCheckPasses.types new file mode 100644 index 00000000000..e0e582916bb --- /dev/null +++ b/tests/baselines/reference/unionRelationshipCheckPasses.types @@ -0,0 +1,10 @@ +=== tests/cases/compiler/unionRelationshipCheckPasses.ts === +const item: { foo?: undefined } | { foo: number } = null as any as { foo?: number | undefined }; +>item : { foo?: undefined; } | { foo: number; } +>foo : undefined +>foo : number +>null as any as { foo?: number | undefined } : { foo?: number | undefined; } +>null as any : any +>null : null +>foo : number | undefined + diff --git a/tests/cases/compiler/unionRelationshipCheckPasses.ts b/tests/cases/compiler/unionRelationshipCheckPasses.ts new file mode 100644 index 00000000000..cec4cffbf08 --- /dev/null +++ b/tests/cases/compiler/unionRelationshipCheckPasses.ts @@ -0,0 +1,2 @@ +// @strict: true +const item: { foo?: undefined } | { foo: number } = null as any as { foo?: number | undefined };