Skip to content

Commit a70c409

Browse files
authored
Fast path for negative case when relating to unions of primtives (microsoft#53192)
1 parent ba42ad3 commit a70c409

File tree

2 files changed

+19
-8
lines changed

2 files changed

+19
-8
lines changed

src/compiler/checker.ts

+18-7
Original file line numberDiff line numberDiff line change
@@ -16582,7 +16582,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1658216582
for (const u of unionTypes) {
1658316583
if (!containsType(u.types, type)) {
1658416584
const primitive = type.flags & TypeFlags.StringLiteral ? stringType :
16585-
type.flags & TypeFlags.NumberLiteral ? numberType :
16585+
type.flags & (TypeFlags.Enum | TypeFlags.NumberLiteral) ? numberType :
1658616586
type.flags & TypeFlags.BigIntLiteral ? bigintType :
1658716587
type.flags & TypeFlags.UniqueESSymbol ? esSymbolType :
1658816588
undefined;
@@ -16618,10 +16618,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1661816618
return false;
1661916619
}
1662016620

16621-
function eachIsUnionContaining(types: Type[], flag: TypeFlags) {
16622-
return every(types, t => !!(t.flags & TypeFlags.Union) && some((t as UnionType).types, tt => !!(tt.flags & flag)));
16623-
}
16624-
1662516621
function removeFromEach(types: Type[], flag: TypeFlags) {
1662616622
for (let i = 0; i < types.length; i++) {
1662716623
types[i] = filterType(types[i], t => !(t.flags & flag));
@@ -16753,12 +16749,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1675316749
// reduced we'll never reduce again, so this occurs at most once.
1675416750
result = getIntersectionType(typeSet, aliasSymbol, aliasTypeArguments);
1675516751
}
16756-
else if (eachIsUnionContaining(typeSet, TypeFlags.Undefined)) {
16752+
else if (every(typeSet, t => !!(t.flags & TypeFlags.Union && (t as UnionType).types[0].flags & TypeFlags.Undefined))) {
1675716753
const containedUndefinedType = some(typeSet, containsMissingType) ? missingType : undefinedType;
1675816754
removeFromEach(typeSet, TypeFlags.Undefined);
1675916755
result = getUnionType([getIntersectionType(typeSet), containedUndefinedType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments);
1676016756
}
16761-
else if (eachIsUnionContaining(typeSet, TypeFlags.Null)) {
16757+
else if (every(typeSet, t => !!(t.flags & TypeFlags.Union && ((t as UnionType).types[0].flags & TypeFlags.Null || (t as UnionType).types[1].flags & TypeFlags.Null)))) {
1676216758
removeFromEach(typeSet, TypeFlags.Null);
1676316759
result = getUnionType([getIntersectionType(typeSet), nullType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments);
1676416760
}
@@ -20855,6 +20851,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2085520851
if (containsType(targetTypes, source)) {
2085620852
return Ternary.True;
2085720853
}
20854+
if (getObjectFlags(target) & ObjectFlags.PrimitiveUnion && !(source.flags & TypeFlags.EnumLiteral) && (
20855+
source.flags & (TypeFlags.StringLiteral | TypeFlags.BooleanLiteral | TypeFlags.BigIntLiteral) ||
20856+
(relation === subtypeRelation || relation === strictSubtypeRelation) && source.flags & TypeFlags.NumberLiteral)) {
20857+
// When relating a literal type to a union of primitive types, we know the relation is false unless
20858+
// the union contains the base primitive type or the literal type in one of its fresh/regular forms.
20859+
// We exclude numeric literals for non-subtype relations because numeric literals are assignable to
20860+
// numeric enum literals with the same value. Similarly, we exclude enum literal types because
20861+
// identically named enum types are related (see isEmumTypeRelatedTo).
20862+
const alternateForm = source === (source as StringLiteralType).regularType ? (source as StringLiteralType).freshType : (source as StringLiteralType).regularType;
20863+
const primitive = source.flags & TypeFlags.StringLiteral ? stringType :
20864+
source.flags & TypeFlags.NumberLiteral ? numberType :
20865+
source.flags & TypeFlags.BigIntLiteral ? bigintType :
20866+
undefined;
20867+
return primitive && containsType(targetTypes, primitive) || alternateForm && containsType(targetTypes, alternateForm) ? Ternary.True : Ternary.False;
20868+
}
2085820869
const match = getMatchingUnionConstituentForType(target as UnionType, source);
2085920870
if (match) {
2086020871
const related = isRelatedTo(source, match, RecursionFlags.Target, /*reportErrors*/ false);

src/compiler/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6130,7 +6130,7 @@ export const enum TypeFlags {
61306130
/** @internal */
61316131
IncludesInstantiable = Substitution,
61326132
/** @internal */
6133-
NotPrimitiveUnion = Any | Unknown | Enum | Void | Never | Object | Intersection | IncludesInstantiable,
6133+
NotPrimitiveUnion = Any | Unknown | Void | Never | Object | Intersection | IncludesInstantiable,
61346134
}
61356135

61366136
export type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression;

0 commit comments

Comments
 (0)