Skip to content

Skip comparing optional property flag when comparing against discriminant properties #38101

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16540,7 +16540,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) {
Expand Down Expand Up @@ -16638,7 +16638,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) {
Expand Down Expand Up @@ -16681,7 +16681,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..
Expand Down Expand Up @@ -16811,7 +16811,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;
}
Expand Down
7 changes: 7 additions & 0 deletions tests/baselines/reference/unionRelationshipCheckPasses.js
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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))

10 changes: 10 additions & 0 deletions tests/baselines/reference/unionRelationshipCheckPasses.types
Original file line number Diff line number Diff line change
@@ -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

2 changes: 2 additions & 0 deletions tests/cases/compiler/unionRelationshipCheckPasses.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// @strict: true
const item: { foo?: undefined } | { foo: number } = null as any as { foo?: number | undefined };