@@ -1339,6 +1339,7 @@ export const enum CheckMode {
1339
1339
// e.g. in `const { a, ...rest } = foo`, when checking the type of `foo` to determine the type of `rest`,
1340
1340
// we need to preserve generic types instead of substituting them for constraints
1341
1341
TypeOnly = 1 << 6, // Called from getTypeOfExpression, diagnostics may be omitted
1342
+ SkipConstraintsSubstitution = 1 << 7,
1342
1343
}
1343
1344
1344
1345
/** @internal */
@@ -30365,21 +30366,27 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
30365
30366
return contextualType && !isGenericType(contextualType);
30366
30367
}
30367
30368
30368
- function getNarrowableTypeForReference(type: Type, reference: Node, checkMode?: CheckMode) {
30369
- if (isNoInferType(type)) {
30370
- type = (type as SubstitutionType).baseType;
30371
- }
30369
+ function shouldSubstituteConstraints(type: Type, reference: Node, checkMode?: CheckMode) {
30372
30370
// When the type of a reference is or contains an instantiable type with a union type constraint, and
30373
30371
// when the reference is in a constraint position (where it is known we'll obtain the apparent type) or
30374
30372
// has a contextual type containing no top-level instantiables (meaning constraints will determine
30375
30373
// assignability), we substitute constraints for all instantiables in the type of the reference to give
30376
30374
// control flow analysis an opportunity to narrow it further. For example, for a reference of a type
30377
30375
// parameter type 'T extends string | undefined' with a contextual type 'string', we substitute
30378
30376
// 'string | undefined' to give control flow analysis the opportunity to narrow to type 'string'.
30379
- const substituteConstraints = !(checkMode && checkMode & CheckMode.Inferential) &&
30377
+ return !(checkMode && checkMode & CheckMode.Inferential) &&
30380
30378
someType(type, isGenericTypeWithUnionConstraint) &&
30381
30379
(isConstraintPosition(type, reference) || hasContextualTypeWithNoGenericTypes(reference, checkMode));
30382
- return substituteConstraints ? mapType(type, getBaseConstraintOrType) : type;
30380
+ }
30381
+
30382
+ function getNarrowableTypeForReference(type: Type, reference: Node, checkMode = CheckMode.Normal) {
30383
+ if (isNoInferType(type)) {
30384
+ type = (type as SubstitutionType).baseType;
30385
+ }
30386
+ if (checkMode & CheckMode.SkipConstraintsSubstitution) {
30387
+ return type;
30388
+ }
30389
+ return shouldSubstituteConstraints(type, reference, checkMode) ? mapType(type, getBaseConstraintOrType) : type;
30383
30390
}
30384
30391
30385
30392
function isExportOrExportExpression(location: Node) {
@@ -30811,7 +30818,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
30811
30818
const type = getTypeOfSymbol(symbol);
30812
30819
const declaration = symbol.valueDeclaration;
30813
30820
if (declaration) {
30814
- // If we have a non-rest binding element with no initializer declared as a const variable or a const-like
30821
+ // If we have a binding element with no initializer declared as a const variable or a const-like
30815
30822
// parameter (a parameter for which there are no assignments in the function body), and if the parent type
30816
30823
// for the destructuring is a union type, one or more of the binding elements may represent discriminant
30817
30824
// properties, and we want the effects of conditional checks on such discriminants to affect the types of
@@ -30834,19 +30841,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
30834
30841
// the binding pattern AST instance for '{ kind, payload }' as a pseudo-reference and narrow this reference
30835
30842
// as if it occurred in the specified location. We then recompute the narrowed binding element type by
30836
30843
// destructuring from the narrowed parent type.
30837
- if (isBindingElement(declaration) && !declaration.initializer && !declaration.dotDotDotToken && declaration.parent.elements.length >= 2) {
30844
+ if (isBindingElement(declaration) && !declaration.initializer && declaration.parent.elements.length >= 2) {
30838
30845
const parent = declaration.parent.parent;
30839
30846
const rootDeclaration = getRootDeclaration(parent);
30840
30847
if (rootDeclaration.kind === SyntaxKind.VariableDeclaration && getCombinedNodeFlagsCached(rootDeclaration) & NodeFlags.Constant || rootDeclaration.kind === SyntaxKind.Parameter) {
30841
30848
const links = getNodeLinks(parent);
30842
30849
if (!(links.flags & NodeCheckFlags.InCheckIdentifier)) {
30843
30850
links.flags |= NodeCheckFlags.InCheckIdentifier;
30844
- const parentType = getTypeForBindingElementParent(parent, CheckMode.Normal);
30845
- const parentTypeConstraint = parentType && mapType(parentType, getBaseConstraintOrType);
30851
+ const parentType = getTypeForBindingElementParent(parent, shouldSubstituteConstraints(type, location) ? CheckMode.Normal : CheckMode.SkipConstraintsSubstitution);
30852
+ const parentNarrowableType = parentType && getNarrowableTypeForReference(parentType, location);
30853
+
30846
30854
links.flags &= ~NodeCheckFlags.InCheckIdentifier;
30847
- if (parentTypeConstraint && parentTypeConstraint .flags & TypeFlags.Union && !(rootDeclaration.kind === SyntaxKind.Parameter && isSomeSymbolAssigned(rootDeclaration))) {
30855
+ if (parentNarrowableType && parentNarrowableType .flags & TypeFlags.Union && !(rootDeclaration.kind === SyntaxKind.Parameter && isSomeSymbolAssigned(rootDeclaration))) {
30848
30856
const pattern = declaration.parent;
30849
- const narrowedType = getFlowTypeOfReference(pattern, parentTypeConstraint, parentTypeConstraint , /*flowContainer*/ undefined, location.flowNode);
30857
+ const narrowedType = getFlowTypeOfReference(pattern, parentNarrowableType, parentNarrowableType , /*flowContainer*/ undefined, location.flowNode);
30850
30858
if (narrowedType.flags & TypeFlags.Never) {
30851
30859
return neverType;
30852
30860
}
0 commit comments