Properly handle missingType in intersections (#46052)

* Properly handle missingType in intersections

* Add regression tests

* Accept new baselines

* Fix tests
This commit is contained in:
Anders Hejlsberg
2021-09-26 14:13:42 -07:00
committed by GitHub
parent 2be3d45aa7
commit 2f0c6070cb
7 changed files with 188 additions and 18 deletions

View File

@@ -14030,7 +14030,6 @@ namespace ts {
// We ignore 'never' types in unions
if (!(flags & TypeFlags.Never)) {
includes |= flags & TypeFlags.IncludesMask;
if (flags & TypeFlags.StructuredOrInstantiable) includes |= TypeFlags.IncludesStructuredOrInstantiable;
if (type === wildcardType) includes |= TypeFlags.IncludesWildcard;
if (!strictNullChecks && flags & TypeFlags.Nullable) {
if (!(getObjectFlags(type) & ObjectFlags.ContainsWideningType)) includes |= TypeFlags.IncludesNonWideningType;
@@ -14337,13 +14336,19 @@ namespace ts {
if (flags & TypeFlags.AnyOrUnknown) {
if (type === wildcardType) includes |= TypeFlags.IncludesWildcard;
}
else if ((strictNullChecks || !(flags & TypeFlags.Nullable)) && !typeSet.has(type.id.toString())) {
if (type.flags & TypeFlags.Unit && includes & TypeFlags.Unit) {
// We have seen two distinct unit types which means we should reduce to an
// empty intersection. Adding TypeFlags.NonPrimitive causes that to happen.
includes |= TypeFlags.NonPrimitive;
else if (strictNullChecks || !(flags & TypeFlags.Nullable)) {
if (exactOptionalPropertyTypes && type === missingType) {
includes |= TypeFlags.IncludesMissingType;
type = undefinedType;
}
if (!typeSet.has(type.id.toString())) {
if (type.flags & TypeFlags.Unit && includes & TypeFlags.Unit) {
// We have seen two distinct unit types which means we should reduce to an
// empty intersection. Adding TypeFlags.NonPrimitive causes that to happen.
includes |= TypeFlags.NonPrimitive;
}
typeSet.set(type.id.toString(), type);
}
typeSet.set(type.id.toString(), type);
}
includes |= flags & TypeFlags.IncludesMask;
}
@@ -14418,14 +14423,14 @@ namespace ts {
return false;
}
function extractIrreducible(types: Type[], flag: TypeFlags) {
if (every(types, t => !!(t.flags & TypeFlags.Union) && some((t as UnionType).types, tt => !!(tt.flags & flag)))) {
for (let i = 0; i < types.length; i++) {
types[i] = filterType(types[i], t => !(t.flags & flag));
}
return true;
function eachIsUnionContaining(types: Type[], flag: TypeFlags) {
return every(types, t => !!(t.flags & TypeFlags.Union) && some((t as UnionType).types, tt => !!(tt.flags & flag)));
}
function removeFromEach(types: Type[], flag: TypeFlags) {
for (let i = 0; i < types.length; i++) {
types[i] = filterType(types[i], t => !(t.flags & flag));
}
return false;
}
// If the given list of types contains more than one union of primitive types, replace the
@@ -14535,6 +14540,9 @@ namespace ts {
if (includes & TypeFlags.IncludesEmptyObject && includes & TypeFlags.Object) {
orderedRemoveItemAt(typeSet, findIndex(typeSet, isEmptyAnonymousObjectType));
}
if (includes & TypeFlags.IncludesMissingType) {
typeSet[typeSet.indexOf(undefinedType)] = missingType;
}
if (typeSet.length === 0) {
return unknownType;
}
@@ -14551,10 +14559,13 @@ namespace ts {
// reduced we'll never reduce again, so this occurs at most once.
result = getIntersectionType(typeSet, aliasSymbol, aliasTypeArguments);
}
else if (extractIrreducible(typeSet, TypeFlags.Undefined)) {
result = getUnionType([getIntersectionType(typeSet), undefinedType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments);
else if (eachIsUnionContaining(typeSet, TypeFlags.Undefined)) {
const undefinedOrMissingType = exactOptionalPropertyTypes && some(typeSet, t => containsType((t as UnionType).types, missingType)) ? missingType : undefinedType;
removeFromEach(typeSet, TypeFlags.Undefined);
result = getUnionType([getIntersectionType(typeSet), undefinedOrMissingType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments);
}
else if (extractIrreducible(typeSet, TypeFlags.Null)) {
else if (eachIsUnionContaining(typeSet, TypeFlags.Null)) {
removeFromEach(typeSet, TypeFlags.Null);
result = getUnionType([getIntersectionType(typeSet), nullType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments);
}
else {

View File

@@ -5181,7 +5181,7 @@ namespace ts {
IncludesMask = Any | Unknown | Primitive | Never | Object | Union | Intersection | NonPrimitive | TemplateLiteral,
// The following flags are used for different purposes during union and intersection type construction
/* @internal */
IncludesStructuredOrInstantiable = TypeParameter,
IncludesMissingType = TypeParameter,
/* @internal */
IncludesNonWideningType = Index,
/* @internal */

View File

@@ -329,4 +329,27 @@ tests/cases/compiler/strictOptionalProperties1.ts(211,1): error TS2322: Type 'st
~
!!! error TS2322: Type 'string | boolean | undefined' is not assignable to type '{ [x: string]: string | number; }'.
!!! error TS2322: Type 'undefined' is not assignable to type '{ [x: string]: string | number; }'.
// Repro from #46004
interface PropsFromReact {
onClick?: () => void;
}
interface PropsFromMaterialUI {
onClick?: (() => void) | undefined;
}
type TheTypeFromMaterialUI = PropsFromReact & PropsFromMaterialUI;
interface NavBottomListItem extends TheTypeFromMaterialUI {
value: string;
}
// Repro from #46004
type UA = undefined; // Explicit undefined type
type UB = { x?: never }['x']; // undefined from missing property
type UC = UA & UB; // undefined

View File

@@ -210,6 +210,29 @@ a = b;
a = c;
a = d; // Error
a = e; // Error
// Repro from #46004
interface PropsFromReact {
onClick?: () => void;
}
interface PropsFromMaterialUI {
onClick?: (() => void) | undefined;
}
type TheTypeFromMaterialUI = PropsFromReact & PropsFromMaterialUI;
interface NavBottomListItem extends TheTypeFromMaterialUI {
value: string;
}
// Repro from #46004
type UA = undefined; // Explicit undefined type
type UB = { x?: never }['x']; // undefined from missing property
type UC = UA & UB; // undefined
//// [strictOptionalProperties1.js]
@@ -453,3 +476,18 @@ declare var e: {
a: number;
b?: string | undefined;
};
interface PropsFromReact {
onClick?: () => void;
}
interface PropsFromMaterialUI {
onClick?: (() => void) | undefined;
}
declare type TheTypeFromMaterialUI = PropsFromReact & PropsFromMaterialUI;
interface NavBottomListItem extends TheTypeFromMaterialUI {
value: string;
}
declare type UA = undefined;
declare type UB = {
x?: never;
}['x'];
declare type UC = UA & UB;

View File

@@ -670,3 +670,46 @@ a = e; // Error
>a : Symbol(a, Decl(strictOptionalProperties1.ts, 201, 11))
>e : Symbol(e, Decl(strictOptionalProperties1.ts, 189, 13))
// Repro from #46004
interface PropsFromReact {
>PropsFromReact : Symbol(PropsFromReact, Decl(strictOptionalProperties1.ts, 210, 6))
onClick?: () => void;
>onClick : Symbol(PropsFromReact.onClick, Decl(strictOptionalProperties1.ts, 214, 26))
}
interface PropsFromMaterialUI {
>PropsFromMaterialUI : Symbol(PropsFromMaterialUI, Decl(strictOptionalProperties1.ts, 216, 1))
onClick?: (() => void) | undefined;
>onClick : Symbol(PropsFromMaterialUI.onClick, Decl(strictOptionalProperties1.ts, 218, 31))
}
type TheTypeFromMaterialUI = PropsFromReact & PropsFromMaterialUI;
>TheTypeFromMaterialUI : Symbol(TheTypeFromMaterialUI, Decl(strictOptionalProperties1.ts, 220, 1))
>PropsFromReact : Symbol(PropsFromReact, Decl(strictOptionalProperties1.ts, 210, 6))
>PropsFromMaterialUI : Symbol(PropsFromMaterialUI, Decl(strictOptionalProperties1.ts, 216, 1))
interface NavBottomListItem extends TheTypeFromMaterialUI {
>NavBottomListItem : Symbol(NavBottomListItem, Decl(strictOptionalProperties1.ts, 222, 66))
>TheTypeFromMaterialUI : Symbol(TheTypeFromMaterialUI, Decl(strictOptionalProperties1.ts, 220, 1))
value: string;
>value : Symbol(NavBottomListItem.value, Decl(strictOptionalProperties1.ts, 224, 59))
}
// Repro from #46004
type UA = undefined; // Explicit undefined type
>UA : Symbol(UA, Decl(strictOptionalProperties1.ts, 226, 1))
type UB = { x?: never }['x']; // undefined from missing property
>UB : Symbol(UB, Decl(strictOptionalProperties1.ts, 230, 20))
>x : Symbol(x, Decl(strictOptionalProperties1.ts, 231, 11))
type UC = UA & UB; // undefined
>UC : Symbol(UC, Decl(strictOptionalProperties1.ts, 231, 29))
>UA : Symbol(UA, Decl(strictOptionalProperties1.ts, 226, 1))
>UB : Symbol(UB, Decl(strictOptionalProperties1.ts, 230, 20))

View File

@@ -780,3 +780,35 @@ a = e; // Error
>a : { [x: string]: string | number; }
>e : string | boolean | undefined
// Repro from #46004
interface PropsFromReact {
onClick?: () => void;
>onClick : (() => void) | undefined
}
interface PropsFromMaterialUI {
onClick?: (() => void) | undefined;
>onClick : (() => void) | undefined
}
type TheTypeFromMaterialUI = PropsFromReact & PropsFromMaterialUI;
>TheTypeFromMaterialUI : TheTypeFromMaterialUI
interface NavBottomListItem extends TheTypeFromMaterialUI {
value: string;
>value : string
}
// Repro from #46004
type UA = undefined; // Explicit undefined type
>UA : undefined
type UB = { x?: never }['x']; // undefined from missing property
>UB : undefined
>x : undefined
type UC = UA & UB; // undefined
>UC : undefined

View File

@@ -213,3 +213,26 @@ a = b;
a = c;
a = d; // Error
a = e; // Error
// Repro from #46004
interface PropsFromReact {
onClick?: () => void;
}
interface PropsFromMaterialUI {
onClick?: (() => void) | undefined;
}
type TheTypeFromMaterialUI = PropsFromReact & PropsFromMaterialUI;
interface NavBottomListItem extends TheTypeFromMaterialUI {
value: string;
}
// Repro from #46004
type UA = undefined; // Explicit undefined type
type UB = { x?: never }['x']; // undefined from missing property
type UC = UA & UB; // undefined