From dc3eb3c27eb0723cd8d8ea93c200dab3445719cc Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 28 Mar 2018 13:16:11 -0700 Subject: [PATCH] Erase 'infer T' locations in conditional type constraints --- src/compiler/checker.ts | 14 ++++++++++++-- src/compiler/types.ts | 4 ++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 89c799db46d..4c5f9b93967 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6233,7 +6233,7 @@ namespace ts { } function getDefaultConstraintOfConditionalType(type: ConditionalType) { - return getUnionType([getTrueTypeFromConditionalType(type), getFalseTypeFromConditionalType(type)]); + return getUnionType([getInferredTrueTypeFromConditionalType(type), getFalseTypeFromConditionalType(type)]); } function getConstraintOfDistributiveConditionalType(type: ConditionalType): Type { @@ -8343,16 +8343,19 @@ namespace ts { // If this is a distributive conditional type and the check type is generic we need to defer // resolution of the conditional type such that a later instantiation will properly distribute // over union types. - if (!root.isDistributive || !maybeTypeOfKind(checkType, TypeFlags.Instantiable)) { + const isDeferred = root.isDistributive && maybeTypeOfKind(checkType, TypeFlags.Instantiable); let combinedMapper: TypeMapper; if (root.inferTypeParameters) { const context = createInferenceContext(root.inferTypeParameters, /*signature*/ undefined, InferenceFlags.None); + if (!isDeferred) { // We don't want inferences from constraints as they may cause us to eagerly resolve the // conditional type instead of deferring resolution. Also, we always want strict function // types rules (i.e. proper contravariance) for inferences. inferTypes(context.inferences, checkType, extendsType, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict); + } combinedMapper = combineTypeMappers(mapper, context); } + if (!isDeferred) { // Return union of trueType and falseType for 'any' since it matches anything if (checkType.flags & TypeFlags.Any) { return getUnionType([instantiateType(root.trueType, combinedMapper || mapper), instantiateType(root.falseType, mapper)]); @@ -8381,6 +8384,7 @@ namespace ts { result.checkType = erasedCheckType; result.extendsType = extendsType; result.mapper = mapper; + result.combinedMapper = combinedMapper; result.aliasSymbol = root.aliasSymbol; result.aliasTypeArguments = instantiateTypes(root.aliasTypeArguments, mapper); return result; @@ -8394,6 +8398,12 @@ namespace ts { return type.resolvedFalseType || (type.resolvedFalseType = instantiateType(type.root.falseType, type.mapper)); } + function getInferredTrueTypeFromConditionalType(type: ConditionalType) { + return type.combinedMapper ? + type.resolvedInferredTrueType || (type.resolvedInferredTrueType = instantiateType(type.root.trueType, type.combinedMapper)) : + getTrueTypeFromConditionalType(type); + } + function getInferTypeParameters(node: ConditionalTypeNode): TypeParameter[] { let result: TypeParameter[]; if (node.locals) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 51b1d984bf6..7775543dee6 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3878,7 +3878,11 @@ namespace ts { resolvedTrueType?: Type; resolvedFalseType?: Type; /* @internal */ + resolvedInferredTrueType?: Type; + /* @internal */ mapper?: TypeMapper; + /* @internal */ + combinedMapper?: TypeMapper; } // Type parameter substitution (TypeFlags.Substitution)