@@ -17409,7 +17409,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1740917409 return false;
1741017410 }
1741117411
17412- function addTypeToUnion(typeSet: Type[], includes: TypeFlags, type: Type) {
17412+ function addTypeToUnion(typeSet: Type[] | undefined , includes: TypeFlags, type: Type) {
1741317413 const flags = type.flags;
1741417414 // We ignore 'never' types in unions
1741517415 if (!(flags & TypeFlags.Never)) {
@@ -17421,7 +17421,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1742117421 if (!strictNullChecks && flags & TypeFlags.Nullable) {
1742217422 if (!(getObjectFlags(type) & ObjectFlags.ContainsWideningType)) includes |= TypeFlags.IncludesNonWideningType;
1742317423 }
17424- else {
17424+ else if (typeSet) {
1742517425 const len = typeSet.length;
1742617426 const index = len && type.id > typeSet[len - 1].id ? ~len : binarySearch(typeSet, type, getTypeId, compareValues);
1742717427 if (index < 0) {
@@ -17434,7 +17434,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1743417434
1743517435 // Add the given types to the given type set. Order is preserved, duplicates are removed,
1743617436 // and nested types of the given kind are flattened into the set.
17437- function addTypesToUnion(typeSet: Type[], includes: TypeFlags, types: readonly Type[]): TypeFlags {
17437+ function addTypesToUnion(typeSet: Type[] | undefined , includes: TypeFlags, types: readonly Type[]): TypeFlags {
1743817438 let lastType: Type | undefined;
1743917439 for (const type of types) {
1744017440 // We skip the type if it is the same as the last type we processed. This simple test particularly
@@ -19644,6 +19644,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1964419644 return !!(type.flags & TypeFlags.Freshable) && (type as LiteralType).freshType === type;
1964519645 }
1964619646
19647+ function isRegularLiteralType(type: Type) {
19648+ return !!(type.flags & TypeFlags.Freshable) && (type as LiteralType).regularType === type;
19649+ }
19650+
1964719651 function getStringLiteralType(value: string): StringLiteralType {
1964819652 let type;
1964919653 return stringLiteralTypes.get(value) ||
@@ -25036,10 +25040,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2503625040 return filterType(type, t => hasTypeFacts(t, TypeFacts.Truthy));
2503725041 }
2503825042
25039- function extractDefinitelyFalsyTypes(type: Type): Type {
25040- return mapType(type, getDefinitelyFalsyPartOfType);
25041- }
25042-
2504325043 function getDefinitelyFalsyPartOfType(type: Type): Type {
2504425044 return type.flags & TypeFlags.String ? emptyStringType :
2504525045 type.flags & TypeFlags.Number ? zeroType :
@@ -40160,7 +40160,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4016040160 case SyntaxKind.AmpersandAmpersandToken:
4016140161 case SyntaxKind.AmpersandAmpersandEqualsToken: {
4016240162 const resultType = hasTypeFacts(leftType, TypeFacts.Truthy) ?
40163- getUnionType([extractDefinitelyFalsyTypes( strictNullChecks ? leftType : getBaseTypeOfLiteralType(rightType)) , rightType] ) :
40163+ getUnionOfLeftAndRightTypes( strictNullChecks ? leftType : getBaseTypeOfLiteralType(rightType), rightType, getDefinitelyFalsyPartOfType ) :
4016440164 leftType;
4016540165 if (operator === SyntaxKind.AmpersandAmpersandEqualsToken) {
4016640166 checkAssignmentOperator(rightType);
@@ -40170,7 +40170,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4017040170 case SyntaxKind.BarBarToken:
4017140171 case SyntaxKind.BarBarEqualsToken: {
4017240172 const resultType = hasTypeFacts(leftType, TypeFacts.Falsy) ?
40173- getUnionType([getNonNullableType(removeDefinitelyFalsyTypes( leftType)) , rightType] , UnionReduction.Subtype) :
40173+ getUnionOfLeftAndRightTypes( leftType, rightType, t => hasTypeFacts(t, TypeFacts.Truthy) ? getNonNullableType(t) : neverType , UnionReduction.Subtype) :
4017440174 leftType;
4017540175 if (operator === SyntaxKind.BarBarEqualsToken) {
4017640176 checkAssignmentOperator(rightType);
@@ -40180,7 +40180,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4018040180 case SyntaxKind.QuestionQuestionToken:
4018140181 case SyntaxKind.QuestionQuestionEqualsToken: {
4018240182 const resultType = hasTypeFacts(leftType, TypeFacts.EQUndefinedOrNull) ?
40183- getUnionType([getNonNullableType( leftType) , rightType] , UnionReduction.Subtype) :
40183+ getUnionOfLeftAndRightTypes( leftType, rightType, getNonNullableType , UnionReduction.Subtype) :
4018440184 leftType;
4018540185 if (operator === SyntaxKind.QuestionQuestionEqualsToken) {
4018640186 checkAssignmentOperator(rightType);
@@ -40415,6 +40415,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4041540415 }
4041640416 return false;
4041740417 }
40418+
40419+ function getUnionOfLeftAndRightTypes(leftType: Type, rightType: Type, adjustLeft: (type: Type) => Type, unionReduction?: UnionReduction) {
40420+ const rightTypes = rightType.flags & TypeFlags.Union ? (rightType as UnionType).types : [rightType];
40421+ const includes = addTypesToUnion(/*typeSet*/ undefined, 0 as TypeFlags, rightTypes) & (TypeFlags.BaseOfLiteral | TypeFlags.Nullable);
40422+ return getUnionType([
40423+ mapType(
40424+ leftType,
40425+ // when something could be removed from the left type and when it's in the right type it means it would be re-added right away
40426+ // in such a case it's preserved in the mapped left type to help with origin/alias preservation
40427+ t => includes & t.flags || isRegularLiteralType(t) && containsType(rightTypes, (t as LiteralType).freshType) ? t : adjustLeft(t),
40428+ ),
40429+ rightType,
40430+ ], unionReduction);
40431+ }
4041840432 }
4041940433
4042040434 function getBaseTypesIfUnrelated(leftType: Type, rightType: Type, isRelated: (left: Type, right: Type) => boolean): [Type, Type] {
0 commit comments