From 6f681d276a882a14892f77ec65e5151ae5179614 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 30 Mar 2018 15:28:01 -0700 Subject: [PATCH] Constraint of 'T extends U ? T : F' should be 'T & U | F' --- src/compiler/checker.ts | 32 +++++++++++++++++++++++--------- src/compiler/types.ts | 2 +- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b59395fe87c..a556b979941 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6233,7 +6233,12 @@ namespace ts { } function getDefaultConstraintOfConditionalType(type: ConditionalType) { - return getUnionType([getInferredTrueTypeFromConditionalType(type), getFalseTypeFromConditionalType(type)]); + if (!type.resolvedDefaultConstraint) { + const rootTrueType = type.root.trueType; + const rootTrueConstraint = rootTrueType.flags & TypeFlags.Substitution ? (rootTrueType).substitute : rootTrueType; + type.resolvedDefaultConstraint = getUnionType([instantiateType(rootTrueConstraint, type.combinedMapper || type.mapper), getFalseTypeFromConditionalType(type)]); + } + return type.resolvedDefaultConstraint; } function getConstraintOfDistributiveConditionalType(type: ConditionalType): Type { @@ -6246,7 +6251,10 @@ namespace ts { const constraint = getConstraintOfType(type.checkType); if (constraint) { const mapper = createTypeMapper([type.root.checkType], [constraint]); - return getConditionalTypeInstantiation(type, combineTypeMappers(mapper, type.mapper)); + const instantiated = getConditionalTypeInstantiation(type, combineTypeMappers(mapper, type.mapper)); + if (!(instantiated.flags & TypeFlags.Never)) { + return instantiated; + } } } return undefined; @@ -8398,12 +8406,6 @@ 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) { @@ -8416,13 +8418,25 @@ namespace ts { return result; } + function getTopConditionalType(node: Node): ConditionalTypeNode { + let result: ConditionalTypeNode; + while (node) { + if (node.kind === SyntaxKind.ConditionalType) { + result = node; + } + node = node.parent; + } + return result; + } + function getTypeFromConditionalTypeNode(node: ConditionalTypeNode): Type { const links = getNodeLinks(node); if (!links.resolvedType) { const checkType = getTypeFromTypeNode(node.checkType); const aliasTypeArguments = getAliasTypeArgumentsForTypeNode(node); const allOuterTypeParameters = getOuterTypeParameters(node, /*includeThisTypes*/ true); - const outerTypeParameters = aliasTypeArguments ? allOuterTypeParameters : filter(allOuterTypeParameters, tp => isTypeParameterPossiblyReferenced(tp, node)); + const topNode = getTopConditionalType(node); + const outerTypeParameters = aliasTypeArguments ? allOuterTypeParameters : filter(allOuterTypeParameters, tp => isTypeParameterPossiblyReferenced(tp, topNode)); const root: ConditionalRoot = { node, checkType, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7775543dee6..0800c21ee8f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3878,7 +3878,7 @@ namespace ts { resolvedTrueType?: Type; resolvedFalseType?: Type; /* @internal */ - resolvedInferredTrueType?: Type; + resolvedDefaultConstraint?: Type; /* @internal */ mapper?: TypeMapper; /* @internal */