@@ -848,6 +848,8 @@ namespace ts {
848
848
emptyTypeLiteralSymbol.members = createSymbolTable();
849
849
const emptyTypeLiteralType = createAnonymousType(emptyTypeLiteralSymbol, emptySymbols, emptyArray, emptyArray, emptyArray);
850
850
851
+ const unknownUnionType = strictNullChecks ? getUnionType([undefinedType, nullType, createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray)]) : unknownType;
852
+
851
853
const emptyGenericType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray) as ObjectType as GenericType;
852
854
emptyGenericType.instantiations = new Map<string, TypeReference>();
853
855
@@ -14835,6 +14837,7 @@ namespace ts {
14835
14837
t.flags & TypeFlags.Number && includes & TypeFlags.NumberLiteral ||
14836
14838
t.flags & TypeFlags.BigInt && includes & TypeFlags.BigIntLiteral ||
14837
14839
t.flags & TypeFlags.ESSymbol && includes & TypeFlags.UniqueESSymbol ||
14840
+ t.flags & TypeFlags.Void && includes & TypeFlags.Undefined ||
14838
14841
t.flags & TypeFlags.NonPrimitive && includes & TypeFlags.Object ||
14839
14842
isEmptyAnonymousObjectType(t) && includes & TypeFlags.DefinitelyNonNullable;
14840
14843
if (remove) {
@@ -14998,6 +15001,7 @@ namespace ts {
14998
15001
includes & TypeFlags.Number && includes & TypeFlags.NumberLiteral ||
14999
15002
includes & TypeFlags.BigInt && includes & TypeFlags.BigIntLiteral ||
15000
15003
includes & TypeFlags.ESSymbol && includes & TypeFlags.UniqueESSymbol ||
15004
+ includes & TypeFlags.Void && includes & TypeFlags.Undefined ||
15001
15005
includes & TypeFlags.NonPrimitive && includes & TypeFlags.Object ||
15002
15006
includes & TypeFlags.IncludesEmptyObject && includes & TypeFlags.DefinitelyNonNullable) {
15003
15007
removeRedundantSupertypes(typeSet, includes);
@@ -18183,7 +18187,7 @@ namespace ts {
18183
18187
// Since unions and intersections may reduce to `never`, we exclude them here.
18184
18188
if (s & TypeFlags.Undefined && (!strictNullChecks && !(t & TypeFlags.UnionOrIntersection) || t & (TypeFlags.Undefined | TypeFlags.Void))) return true;
18185
18189
if (s & TypeFlags.Null && (!strictNullChecks && !(t & TypeFlags.UnionOrIntersection) || t & TypeFlags.Null)) return true;
18186
- if (s & TypeFlags.Object && t & TypeFlags.NonPrimitive) return true;
18190
+ if (s & TypeFlags.Object && t & TypeFlags.NonPrimitive && !(relation === strictSubtypeRelation && isEmptyAnonymousObjectType(source)) ) return true;
18187
18191
if (relation === assignableRelation || relation === comparableRelation) {
18188
18192
if (s & TypeFlags.Any) return true;
18189
18193
// Type number or any numeric literal type is assignable to any numeric enum type or any
@@ -23505,6 +23509,24 @@ namespace ts {
23505
23509
return filterType(type, t => (getTypeFacts(t) & include) !== 0);
23506
23510
}
23507
23511
23512
+ function getIntersectionWithFacts(type: Type, facts: TypeFacts) {
23513
+ const reduced = getTypeWithFacts(strictNullChecks && type.flags & TypeFlags.Unknown ? unknownUnionType : type, facts);
23514
+ if (strictNullChecks) {
23515
+ switch (facts) {
23516
+ case TypeFacts.NEUndefined:
23517
+ const emptyOrNull = maybeTypeOfKind(reduced, TypeFlags.Null) ? emptyObjectType : getUnionType([emptyObjectType, nullType]);
23518
+ return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQUndefined ? getIntersectionType([t, getTypeFacts(t) & TypeFacts.EQNull ? emptyOrNull : emptyObjectType]): t);
23519
+ case TypeFacts.NENull:
23520
+ const emptyOrUndefined = maybeTypeOfKind(reduced, TypeFlags.Undefined) ? emptyObjectType : getUnionType([emptyObjectType, undefinedType]);
23521
+ return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQNull ? getIntersectionType([t, getTypeFacts(t) & TypeFacts.EQUndefined ? emptyOrUndefined : emptyObjectType]): t);
23522
+ case TypeFacts.NEUndefinedOrNull:
23523
+ case TypeFacts.Truthy:
23524
+ return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQUndefinedOrNull ? getIntersectionType([t, emptyObjectType]): t);
23525
+ }
23526
+ }
23527
+ return reduced;
23528
+ }
23529
+
23508
23530
function getTypeWithDefault(type: Type, defaultExpression: Expression) {
23509
23531
return defaultExpression ?
23510
23532
getUnionType([getNonUndefinedType(type), getTypeOfExpression(defaultExpression)]) :
@@ -24637,6 +24659,9 @@ namespace ts {
24637
24659
return getEvolvingArrayType(getUnionType(map(types, getElementTypeOfEvolvingArrayType)));
24638
24660
}
24639
24661
const result = getUnionType(sameMap(types, finalizeEvolvingArrayType), subtypeReduction);
24662
+ if (result === unknownUnionType) {
24663
+ return unknownType;
24664
+ }
24640
24665
if (result !== declaredType && result.flags & declaredType.flags & TypeFlags.Union && arraysEqual((result as UnionType).types, (declaredType as UnionType).types)) {
24641
24666
return declaredType;
24642
24667
}
@@ -24744,8 +24769,7 @@ namespace ts {
24744
24769
24745
24770
function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type {
24746
24771
if (isMatchingReference(reference, expr)) {
24747
- return type.flags & TypeFlags.Unknown && assumeTrue ? nonNullUnknownType :
24748
- getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy);
24772
+ return getIntersectionWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy);
24749
24773
}
24750
24774
if (strictNullChecks && assumeTrue && optionalChainContainsReference(expr, reference)) {
24751
24775
type = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull);
@@ -24905,9 +24929,6 @@ namespace ts {
24905
24929
assumeTrue = !assumeTrue;
24906
24930
}
24907
24931
const valueType = getTypeOfExpression(value);
24908
- if (assumeTrue && (type.flags & TypeFlags.Unknown) && (operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken) && (valueType.flags & TypeFlags.Null)) {
24909
- return getUnionType([nullType, undefinedType]);
24910
- }
24911
24932
if ((type.flags & TypeFlags.Unknown) && assumeTrue && (operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken)) {
24912
24933
if (valueType.flags & (TypeFlags.Primitive | TypeFlags.NonPrimitive)) {
24913
24934
return valueType;
@@ -24927,7 +24948,7 @@ namespace ts {
24927
24948
valueType.flags & TypeFlags.Null ?
24928
24949
assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull :
24929
24950
assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined;
24930
- return type.flags & TypeFlags.Unknown && facts & (TypeFacts.NENull | TypeFacts.NEUndefinedOrNull) ? nonNullUnknownType : getTypeWithFacts (type, facts);
24951
+ return getIntersectionWithFacts (type, facts);
24931
24952
}
24932
24953
if (assumeTrue) {
24933
24954
const filterFn: (t: Type) => boolean = operator === SyntaxKind.EqualsEqualsToken ?
@@ -24956,17 +24977,11 @@ namespace ts {
24956
24977
if (type.flags & TypeFlags.Any && literal.text === "function") {
24957
24978
return type;
24958
24979
}
24959
- if (assumeTrue && type.flags & TypeFlags.Unknown && literal.text === "object") {
24960
- // The non-null unknown type is used to track whether a previous narrowing operation has removed the null type
24961
- // from the unknown type. For example, the expression `x && typeof x === 'object'` first narrows x to the non-null
24962
- // unknown type, and then narrows that to the non-primitive type.
24963
- return type === nonNullUnknownType ? nonPrimitiveType : getUnionType([nonPrimitiveType, nullType]);
24964
- }
24965
24980
const facts = assumeTrue ?
24966
24981
typeofEQFacts.get(literal.text) || TypeFacts.TypeofEQHostObject :
24967
24982
typeofNEFacts.get(literal.text) || TypeFacts.TypeofNEHostObject;
24968
24983
const impliedType = getImpliedTypeFromTypeofGuard(type, literal.text);
24969
- return getTypeWithFacts(assumeTrue && impliedType ? mapType (type, narrowUnionMemberByTypeof( impliedType) ) : type, facts);
24984
+ return getTypeWithFacts(assumeTrue && impliedType ? narrowTypeByImpliedType (type, impliedType) : type, facts);
24970
24985
}
24971
24986
24972
24987
function narrowTypeBySwitchOptionalChainContainment(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number, clauseCheck: (type: Type) => boolean) {
@@ -25019,10 +25034,10 @@ namespace ts {
25019
25034
25020
25035
function getImpliedTypeFromTypeofGuard(type: Type, text: string) {
25021
25036
switch (text) {
25037
+ case "object":
25038
+ return type.flags & TypeFlags.Any ? type : getUnionType([nullType, nonPrimitiveType]);
25022
25039
case "function":
25023
25040
return type.flags & TypeFlags.Any ? type : globalFunctionType;
25024
- case "object":
25025
- return type.flags & TypeFlags.Unknown ? getUnionType([nonPrimitiveType, nullType]) : type;
25026
25041
default:
25027
25042
return typeofTypesByName.get(text);
25028
25043
}
@@ -25034,22 +25049,24 @@ namespace ts {
25034
25049
// the guard. For example: narrowing `{} | undefined` by `"boolean"` should produce the type `boolean`, not
25035
25050
// the filtered type `{}`. For this reason we narrow constituents of the union individually, in addition to
25036
25051
// filtering by type-facts.
25037
- function narrowUnionMemberByTypeof( candidate: Type) {
25038
- return (type: Type) => {
25039
- if (isTypeSubtypeOf(type, candidate)) {
25040
- return type;
25041
- }
25042
- if (isTypeSubtypeOf( candidate, type )) {
25043
- return candidate ;
25052
+ function narrowTypeByImpliedType(type: Type, candidate: Type) {
25053
+ if (type.flags & TypeFlags.AnyOrUnknown) {
25054
+ return candidate;
25055
+ }
25056
+ return mapType(type, t => {
25057
+ if (isTypeRelatedTo(t, candidate, strictSubtypeRelation )) {
25058
+ return t ;
25044
25059
}
25045
- if (type.flags & TypeFlags.Instantiable) {
25046
- const constraint = getBaseConstraintOfType(type) || anyType;
25047
- if (isTypeSubtypeOf(candidate, constraint)) {
25048
- return getIntersectionType([type, candidate]);
25060
+ return mapType(candidate, c => {
25061
+ if (!areTypesComparable(t, c)) {
25062
+ return neverType;
25049
25063
}
25050
- }
25051
- return type;
25052
- };
25064
+ if ((c.flags & TypeFlags.Primitive || c === globalFunctionType) && t.flags & TypeFlags.Object && !isEmptyAnonymousObjectType(t)) {
25065
+ return isTypeSubtypeOf(c, t) ? c : neverType;
25066
+ }
25067
+ return getIntersectionType([t, c]);
25068
+ });
25069
+ });
25053
25070
}
25054
25071
25055
25072
function narrowBySwitchOnTypeOf(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): Type {
@@ -25109,7 +25126,7 @@ namespace ts {
25109
25126
because it is caught in the first clause.
25110
25127
*/
25111
25128
const impliedType = getTypeWithFacts(getUnionType(clauseWitnesses.map(text => getImpliedTypeFromTypeofGuard(type, text) || type)), switchFacts);
25112
- return getTypeWithFacts(mapType (type, narrowUnionMemberByTypeof( impliedType) ), switchFacts);
25129
+ return getTypeWithFacts(narrowTypeByImpliedType (type, impliedType), switchFacts);
25113
25130
}
25114
25131
25115
25132
function isMatchingConstructorReference(expr: Expression) {
0 commit comments