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

This commit is contained in:
Wesley Wigham 2020-04-24 18:33:30 -07:00 committed by GitHub
parent 1b8c68d746
commit 4a5eeb0bb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 30 additions and 4 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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))

View File

@ -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

View File

@ -0,0 +1,2 @@
// @strict: true
const item: { foo?: undefined } | { foo: number } = null as any as { foo?: number | undefined };