Skip to content

Commit 356bba1

Browse files
committed
Merge branch 'master' into correct-narrowing-for-typeof-unknown-in-switch
2 parents 6fdc7e3 + 6adb9d1 commit 356bba1

34 files changed

+610
-89
lines changed

src/compiler/checker.ts

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,8 @@ namespace ts {
601601
FunctionFacts = FunctionStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
602602
UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy,
603603
NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy,
604+
EmptyObjectStrictFacts = All & ~(EQUndefined | EQNull | EQUndefinedOrNull),
605+
EmptyObjectFacts = All,
604606
}
605607

606608
const typeofEQFacts = createMapFromTemplate({
@@ -11836,8 +11838,12 @@ namespace ts {
1183611838
const simplified = getSimplifiedType((<IndexType>target).type);
1183711839
const constraint = simplified !== (<IndexType>target).type ? simplified : getConstraintOfType((<IndexType>target).type);
1183811840
if (constraint) {
11839-
if (result = isRelatedTo(source, getIndexType(constraint, (target as IndexType).stringsOnly), reportErrors)) {
11840-
return result;
11841+
// We require Ternary.True here such that circular constraints don't cause
11842+
// false positives. For example, given 'T extends { [K in keyof T]: string }',
11843+
// 'keyof T' has itself as its constraint and produces a Ternary.Maybe when
11844+
// related to other types.
11845+
if (isRelatedTo(source, getIndexType(constraint, (target as IndexType).stringsOnly), reportErrors) === Ternary.True) {
11846+
return Ternary.True;
1184111847
}
1184211848
}
1184311849
}
@@ -14241,9 +14247,11 @@ namespace ts {
1424114247
(type === falseType || type === regularFalseType) ? TypeFacts.FalseFacts : TypeFacts.TrueFacts;
1424214248
}
1424314249
if (flags & TypeFlags.Object) {
14244-
return isFunctionObjectType(<ObjectType>type) ?
14245-
strictNullChecks ? TypeFacts.FunctionStrictFacts : TypeFacts.FunctionFacts :
14246-
strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts;
14250+
return getObjectFlags(type) & ObjectFlags.Anonymous && isEmptyObjectType(<ObjectType>type) ?
14251+
strictNullChecks ? TypeFacts.EmptyObjectStrictFacts : TypeFacts.EmptyObjectFacts :
14252+
isFunctionObjectType(<ObjectType>type) ?
14253+
strictNullChecks ? TypeFacts.FunctionStrictFacts : TypeFacts.FunctionFacts :
14254+
strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts;
1424714255
}
1424814256
if (flags & (TypeFlags.Void | TypeFlags.Undefined)) {
1424914257
return TypeFacts.UndefinedFacts;
@@ -15163,23 +15171,24 @@ namespace ts {
1516315171
return getTypeWithFacts(assumeTrue ? mapType(type, narrowTypeForTypeof) : type, facts);
1516415172

1516515173
function narrowTypeForTypeof(type: Type) {
15166-
if (assumeTrue && !(type.flags & TypeFlags.Union)) {
15167-
if (type.flags & TypeFlags.Unknown && literal.text === "object") {
15168-
return getUnionType([nonPrimitiveType, nullType]);
15169-
}
15170-
// We narrow a non-union type to an exact primitive type if the non-union type
15171-
// is a supertype of that primitive type. For example, type 'any' can be narrowed
15172-
// to one of the primitive types.
15173-
const targetType = literal.text === "function" ? globalFunctionType : typeofTypesByName.get(literal.text);
15174-
if (targetType) {
15175-
if (isTypeSubtypeOf(targetType, type)) {
15176-
return isTypeAny(type) ? targetType : getIntersectionType([type, targetType]); // Intersection to handle `string` being a subtype of `keyof T`
15177-
}
15178-
if (type.flags & TypeFlags.Instantiable) {
15179-
const constraint = getBaseConstraintOfType(type) || anyType;
15180-
if (isTypeSubtypeOf(targetType, constraint)) {
15181-
return getIntersectionType([type, targetType]);
15182-
}
15174+
if (type.flags & TypeFlags.Unknown && literal.text === "object") {
15175+
return getUnionType([nonPrimitiveType, nullType]);
15176+
}
15177+
// We narrow a non-union type to an exact primitive type if the non-union type
15178+
// is a supertype of that primitive type. For example, type 'any' can be narrowed
15179+
// to one of the primitive types.
15180+
const targetType = literal.text === "function" ? globalFunctionType : typeofTypesByName.get(literal.text);
15181+
if (targetType) {
15182+
if (isTypeSubtypeOf(type, targetType)) {
15183+
return type;
15184+
}
15185+
if (isTypeSubtypeOf(targetType, type)) {
15186+
return targetType;
15187+
}
15188+
if (type.flags & TypeFlags.Instantiable) {
15189+
const constraint = getBaseConstraintOfType(type) || anyType;
15190+
if (isTypeSubtypeOf(targetType, constraint)) {
15191+
return getIntersectionType([type, targetType]);
1518315192
}
1518415193
}
1518515194
}

0 commit comments

Comments
 (0)