mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-10 18:04:18 -05:00
Properly handle missingType in intersections (#46052)
* Properly handle missingType in intersections * Add regression tests * Accept new baselines * Fix tests
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user