diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0612c4d4307..8a7853dfaa5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6096,11 +6096,13 @@ namespace ts { // with its constraint. We do this because if the constraint is a union type it will be distributed // over the conditional type and possibly reduced. For example, 'T extends undefined ? never : T' // removes 'undefined' from T. - const checkType = type.checkType; - if (checkType.flags & TypeFlags.TypeParameter) { - const constraint = getConstraintOfTypeParameter(checkType); + if (isDistributiveConditionalType(type)) { + const constraint = getConstraintOfType(type.checkType); if (constraint) { - return instantiateType(type, createTypeMapper([checkType], [constraint])); + const target = type.target || type; + const mapper = createTypeMapper([target.checkType], [constraint]); + const combinedMapper = type.mapper ? combineTypeMappers(mapper, type.mapper) : mapper; + return instantiateType(target, combinedMapper); } } return undefined; @@ -8237,6 +8239,10 @@ namespace ts { return result; } + function isDistributiveConditionalType(type: ConditionalType) { + return !!((type.target || type).checkType.flags & TypeFlags.TypeParameter); + } + function getInferTypeParameters(node: ConditionalTypeNode): TypeParameter[] { let result: TypeParameter[]; if (node.locals) { @@ -8849,9 +8855,9 @@ namespace ts { // Check if we have a conditional type where the check type is a naked type parameter. If so, // the conditional type is distributive over union types and when T is instantiated to a union // type A | B, we produce (A extends U ? X : Y) | (B extends U ? X : Y). - const checkType = target.checkType; - if (checkType.flags & TypeFlags.TypeParameter) { - const instantiatedType = combinedMapper(checkType); + if (isDistributiveConditionalType(target)) { + const checkType = target.checkType; + const instantiatedType = combinedMapper(checkType); if (checkType !== instantiatedType && instantiatedType.flags & TypeFlags.Union) { return mapType(instantiatedType, t => instantiateConditionalType(target, createReplacementMapper(checkType, t, combinedMapper))); } @@ -9628,17 +9634,43 @@ namespace ts { function isIdenticalTo(source: Type, target: Type): Ternary { let result: Ternary; - if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) { + const flags = source.flags & target.flags; + if (flags & TypeFlags.Object) { return recursiveTypeRelatedTo(source, target, /*reportErrors*/ false); } - if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union || - source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) { + if (flags & (TypeFlags.Union | TypeFlags.Intersection)) { if (result = eachTypeRelatedToSomeType(source, target)) { if (result &= eachTypeRelatedToSomeType(target, source)) { return result; } } } + if (flags & TypeFlags.Index) { + return isRelatedTo((source).type, (target).type, /*reportErrors*/ false); + } + if (flags & TypeFlags.IndexedAccess) { + if (result = isRelatedTo((source).objectType, (target).objectType, /*reportErrors*/ false)) { + if (result &= isRelatedTo((source).indexType, (target).indexType, /*reportErrors*/ false)) { + return result; + } + } + } + if (flags & TypeFlags.Conditional) { + if (result = isRelatedTo((source).checkType, (target).checkType, /*reportErrors*/ false)) { + if (result &= isRelatedTo((source).extendsType, (target).extendsType, /*reportErrors*/ false)) { + if (result &= isRelatedTo((source).trueType, (target).trueType, /*reportErrors*/ false)) { + if (result &= isRelatedTo((source).falseType, (target).falseType, /*reportErrors*/ false)) { + if (isDistributiveConditionalType(source) === isDistributiveConditionalType(target)) { + return result; + } + } + } + } + } + } + if (flags & TypeFlags.Substitution) { + return isRelatedTo((source).substitute, (target).substitute, /*reportErrors*/ false); + } return Ternary.False; } @@ -10024,7 +10056,19 @@ namespace ts { } } } - if (result = isRelatedTo(getDefaultConstraintOfConditionalType(source), target, reportErrors)) { + if (target.flags & TypeFlags.Conditional) { + if (isTypeIdenticalTo((source).checkType, (target).checkType) && + isTypeIdenticalTo((source).extendsType, (target).extendsType)) { + if (result = isRelatedTo((source).trueType, (target).trueType, reportErrors)) { + result &= isRelatedTo((source).falseType, (target).falseType, reportErrors); + } + if (result) { + errorInfo = saveErrorInfo; + return result; + } + } + } + else if (result = isRelatedTo(getDefaultConstraintOfConditionalType(source), target, reportErrors)) { errorInfo = saveErrorInfo; return result; }