Skip to content

Commit f116efb

Browse files
ahejlsbergmprobst
authored andcommitted
Simplify relationship check for conditional type on target side (microsoft#46429)
* Simplify relationship check for conditional type on target side * Accept new baselines * Better support for non-distribution-dependent types * Accept new API baselines * Accept new baselines
1 parent 19be4c6 commit f116efb

File tree

4 files changed

+24
-35
lines changed

4 files changed

+24
-35
lines changed

Diff for: src/compiler/checker.ts

+21-35
Original file line numberDiff line numberDiff line change
@@ -15315,13 +15315,6 @@ namespace ts {
1531515315
return type[cache] = type;
1531615316
}
1531715317

15318-
function isConditionalTypeAlwaysTrueDisregardingInferTypes(type: ConditionalType) {
15319-
const extendsInferParamMapper = type.root.inferTypeParameters && createTypeMapper(type.root.inferTypeParameters, map(type.root.inferTypeParameters, () => wildcardType));
15320-
const checkType = type.checkType;
15321-
const extendsType = type.extendsType;
15322-
return isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(instantiateType(extendsType, extendsInferParamMapper)));
15323-
}
15324-
1532515318
function getSimplifiedConditionalType(type: ConditionalType, writing: boolean) {
1532615319
const checkType = type.checkType;
1532715320
const extendsType = type.extendsType;
@@ -15649,6 +15642,10 @@ namespace ts {
1564915642
const links = getNodeLinks(node);
1565015643
if (!links.resolvedType) {
1565115644
const checkType = getTypeFromTypeNode(node.checkType);
15645+
const isDistributive = !!(checkType.flags & TypeFlags.TypeParameter);
15646+
const isDistributionDependent = isDistributive && (
15647+
isTypeParameterPossiblyReferenced(checkType as TypeParameter, node.trueType) ||
15648+
isTypeParameterPossiblyReferenced(checkType as TypeParameter, node.falseType));
1565215649
const aliasSymbol = getAliasSymbolForTypeNode(node);
1565315650
const aliasTypeArguments = getTypeArgumentsForAliasSymbol(aliasSymbol);
1565415651
const allOuterTypeParameters = getOuterTypeParameters(node, /*includeThisTypes*/ true);
@@ -15657,7 +15654,8 @@ namespace ts {
1565715654
node,
1565815655
checkType,
1565915656
extendsType: getTypeFromTypeNode(node.extendsType),
15660-
isDistributive: !!(checkType.flags & TypeFlags.TypeParameter),
15657+
isDistributive,
15658+
isDistributionDependent,
1566115659
inferTypeParameters: getInferTypeParameters(node),
1566215660
outerTypeParameters,
1566315661
instantiations: undefined,
@@ -19031,33 +19029,21 @@ namespace ts {
1903119029
return Ternary.Maybe;
1903219030
}
1903319031
const c = target as ConditionalType;
19034-
// Check if the conditional is always true or always false but still deferred for distribution purposes
19035-
const skipTrue = !isTypeAssignableTo(getPermissiveInstantiation(c.checkType), getPermissiveInstantiation(c.extendsType));
19036-
const skipFalse = !skipTrue && isConditionalTypeAlwaysTrueDisregardingInferTypes(c);
19037-
19038-
// Instantiate with a replacement mapper if the conditional is distributive, replacing the check type with a clone of itself,
19039-
// this way {x: string | number, y: string | number} -> (T extends T ? { x: T, y: T } : never) appropriately _fails_ when
19040-
// T = string | number (since that will end up distributing and producing `{x: string, y: string} | {x: number, y: number}`,
19041-
// to which `{x: string | number, y: string | number}` isn't assignable)
19042-
let distributionMapper: TypeMapper | undefined;
19043-
const checkVar = getActualTypeVariable(c.root.checkType);
19044-
if (c.root.isDistributive && checkVar.flags & TypeFlags.TypeParameter) {
19045-
const newParam = cloneTypeParameter(checkVar);
19046-
distributionMapper = prependTypeMapping(checkVar, newParam, c.mapper);
19047-
newParam.mapper = distributionMapper;
19048-
}
19049-
19050-
// TODO: Find a nice way to include potential conditional type breakdowns in error output, if they seem good (they usually don't)
19051-
const expanding = isDeeplyNestedType(target, targetStack, targetDepth);
19052-
let localResult: Ternary | undefined = expanding ? Ternary.Maybe : undefined;
19053-
if (skipTrue || expanding || (localResult = isRelatedTo(source, distributionMapper ? instantiateType(getTypeFromTypeNode(c.root.node.trueType), distributionMapper) : getTrueTypeFromConditionalType(c), RecursionFlags.Target, /*reportErrors*/ false))) {
19054-
if (!skipFalse && !expanding) {
19055-
localResult = (localResult || Ternary.Maybe) & isRelatedTo(source, distributionMapper ? instantiateType(getTypeFromTypeNode(c.root.node.falseType), distributionMapper) : getFalseTypeFromConditionalType(c), RecursionFlags.Target, /*reportErrors*/ false);
19056-
}
19057-
}
19058-
if (localResult) {
19059-
resetErrorInfo(saveErrorInfo);
19060-
return localResult;
19032+
// We check for a relationship to a conditional type target only when the conditional type has no
19033+
// 'infer' positions and is not distributive or is distributive but doesn't reference the check type
19034+
// parameter in either of the result types.
19035+
if (!c.root.inferTypeParameters && !c.root.isDistributionDependent) {
19036+
// Check if the conditional is always true or always false but still deferred for distribution purposes.
19037+
const skipTrue = !isTypeAssignableTo(getPermissiveInstantiation(c.checkType), getPermissiveInstantiation(c.extendsType));
19038+
const skipFalse = !skipTrue && isTypeAssignableTo(getRestrictiveInstantiation(c.checkType), getRestrictiveInstantiation(c.extendsType));
19039+
// TODO: Find a nice way to include potential conditional type breakdowns in error output, if they seem good (they usually don't)
19040+
if (result = skipTrue ? Ternary.True : isRelatedTo(source, getTrueTypeFromConditionalType(c), RecursionFlags.Target, /*reportErrors*/ false)) {
19041+
result &= skipFalse ? Ternary.True : isRelatedTo(source, getFalseTypeFromConditionalType(c), RecursionFlags.Target, /*reportErrors*/ false);
19042+
if (result) {
19043+
resetErrorInfo(saveErrorInfo);
19044+
return result;
19045+
}
19046+
}
1906119047
}
1906219048
}
1906319049
else if (target.flags & TypeFlags.TemplateLiteral) {

Diff for: src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5648,6 +5648,7 @@ namespace ts {
56485648
checkType: Type;
56495649
extendsType: Type;
56505650
isDistributive: boolean;
5651+
isDistributionDependent: boolean;
56515652
inferTypeParameters?: TypeParameter[];
56525653
outerTypeParameters?: TypeParameter[];
56535654
instantiations?: Map<Type>;

Diff for: tests/baselines/reference/api/tsserverlibrary.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2747,6 +2747,7 @@ declare namespace ts {
27472747
checkType: Type;
27482748
extendsType: Type;
27492749
isDistributive: boolean;
2750+
isDistributionDependent: boolean;
27502751
inferTypeParameters?: TypeParameter[];
27512752
outerTypeParameters?: TypeParameter[];
27522753
instantiations?: Map<Type>;

Diff for: tests/baselines/reference/api/typescript.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2747,6 +2747,7 @@ declare namespace ts {
27472747
checkType: Type;
27482748
extendsType: Type;
27492749
isDistributive: boolean;
2750+
isDistributionDependent: boolean;
27502751
inferTypeParameters?: TypeParameter[];
27512752
outerTypeParameters?: TypeParameter[];
27522753
instantiations?: Map<Type>;

0 commit comments

Comments
 (0)