diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ccca2c436da5e..7aa8c0ddd682a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10196,6 +10196,7 @@ namespace ts { function getSimplifiedType(type: Type, writing: boolean): Type { return type.flags & TypeFlags.IndexedAccess ? getSimplifiedIndexedAccessType(type, writing) : type.flags & TypeFlags.Conditional ? getSimplifiedConditionalType(type, writing) : + type.flags & TypeFlags.Substitution ? writing ? (type).typeVariable : (type).substitute : type; } @@ -10260,10 +10261,10 @@ namespace ts { } function getSimplifiedConditionalType(type: ConditionalType, writing: boolean) { - const falseType = getFalseTypeFromConditionalType(type); - const trueType = getTrueTypeFromConditionalType(type); const checkType = type.checkType; const extendsType = type.extendsType; + const trueType = getTrueTypeFromConditionalType(type); + const falseType = getFalseTypeFromConditionalType(type); // Simplifications for types of the form `T extends U ? T : never` and `T extends U ? never : T`. if (falseType.flags & TypeFlags.Never && getActualTypeVariable(trueType) === getActualTypeVariable(checkType)) { if (checkType.flags & TypeFlags.Any || isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true @@ -10281,10 +10282,16 @@ namespace ts { return getSimplifiedType(falseType, writing); } } - return type; } + /** + * Invokes union simplification logic to determine if an intersection is considered empty as a union constituent + */ + function isIntersectionEmpty(type1: Type, type2: Type) { + return !!(getUnionType([intersectTypes(type1, type2), neverType]).flags & TypeFlags.Never); + } + function substituteIndexedMappedType(objectType: MappedType, index: Type) { const mapper = createTypeMapper([getTypeParameterFromMappedType(objectType)], [index]); const templateMapper = combineTypeMappers(objectType.mapper, mapper); @@ -10391,36 +10398,12 @@ namespace ts { return type; } - /** - * Invokes union simplification logic to determine if an intersection is considered empty as a union constituent - */ - function isIntersectionEmpty(type1: Type, type2: Type) { - return !!(getUnionType([intersectTypes(type1, type2), neverType]).flags & TypeFlags.Never); - } - function getConditionalType(root: ConditionalRoot, mapper: TypeMapper | undefined): Type { const checkType = instantiateType(root.checkType, mapper); const extendsType = instantiateType(root.extendsType, mapper); if (checkType === wildcardType || extendsType === wildcardType) { return wildcardType; } - // Simplifications for types of the form `T extends U ? T : never` and `T extends U ? never : T`. - if (root.falseType.flags & TypeFlags.Never && getActualTypeVariable(root.trueType) === getActualTypeVariable(root.checkType)) { - if (checkType.flags & TypeFlags.Any || isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true - return checkType; - } - else if (isIntersectionEmpty(checkType, extendsType)) { // Always false - return neverType; - } - } - else if (root.trueType.flags & TypeFlags.Never && getActualTypeVariable(root.falseType) === getActualTypeVariable(root.checkType)) { - if (!(checkType.flags & TypeFlags.Any) && isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true - return neverType; - } - else if (checkType.flags & TypeFlags.Any || isIntersectionEmpty(checkType, extendsType)) { // Always false - return checkType; - } - } const checkTypeInstantiable = maybeTypeOfKind(checkType, TypeFlags.Instantiable | TypeFlags.GenericMappedType); let combinedMapper: TypeMapper | undefined; if (root.inferTypeParameters) { @@ -10461,10 +10444,6 @@ namespace ts { } } // Return a deferred type for a check that is neither definitely true nor definitely false - return getDeferredConditionalType(root, mapper, combinedMapper, checkType, extendsType); - } - - function getDeferredConditionalType(root: ConditionalRoot, mapper: TypeMapper | undefined, combinedMapper: TypeMapper | undefined, checkType: Type, extendsType: Type) { const erasedCheckType = getActualTypeVariable(checkType); const result = createType(TypeFlags.Conditional); result.root = root; @@ -12453,12 +12432,6 @@ namespace ts { if (isFreshLiteralType(target)) { target = (target).regularType; } - if (source.flags & TypeFlags.Substitution) { - source = (source).substitute; - } - if (target.flags & TypeFlags.Substitution) { - target = (target).typeVariable; - } if (source.flags & TypeFlags.Simplifiable) { source = getSimplifiedType(source, /*writing*/ false); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index db1a62937e662..75fb702609b70 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3967,7 +3967,7 @@ namespace ts { /* @internal */ ObjectFlagsType = Nullable | Never | Object | Union | Intersection, /* @internal */ - Simplifiable = IndexedAccess | Conditional, + Simplifiable = IndexedAccess | Conditional | Substitution, // 'Narrowable' types are types where narrowing actually narrows. // This *should* be every type other than null, undefined, void, and never Narrowable = Any | Unknown | StructuredOrInstantiable | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbol | UniqueESSymbol | NonPrimitive,