@@ -175,12 +175,15 @@ namespace ts {
175175 }
176176
177177 const enum CheckMode {
178- Normal = 0, // Normal type checking
179- Contextual = 1 << 0, // Explicitly assigned contextual type, therefore not cacheable
180- Inferential = 1 << 1, // Inferential typing
181- SkipContextSensitive = 1 << 2, // Skip context sensitive function expressions
182- SkipGenericFunctions = 1 << 3, // Skip single signature generic functions
183- IsForSignatureHelp = 1 << 4, // Call resolution for purposes of signature help
178+ Normal = 0, // Normal type checking
179+ Contextual = 1 << 0, // Explicitly assigned contextual type, therefore not cacheable
180+ Inferential = 1 << 1, // Inferential typing
181+ SkipContextSensitive = 1 << 2, // Skip context sensitive function expressions
182+ SkipGenericFunctions = 1 << 3, // Skip single signature generic functions
183+ IsForSignatureHelp = 1 << 4, // Call resolution for purposes of signature help
184+ RestBindingElement = 1 << 5, // Checking a type that is going to be used to determine the type of a rest binding element
185+ // e.g. in `const { a, ...rest } = foo`, when checking the type of `foo` to determine the type of `rest`,
186+ // we need to preserve generic types instead of substituting them for constraints
184187 }
185188
186189 const enum SignatureCheckMode {
@@ -8461,9 +8464,12 @@ namespace ts {
84618464
84628465 // Return the type of a binding element parent. We check SymbolLinks first to see if a type has been
84638466 // assigned by contextual typing.
8464- function getTypeForBindingElementParent(node: BindingElementGrandparent) {
8467+ function getTypeForBindingElementParent(node: BindingElementGrandparent, checkMode: CheckMode) {
8468+ if (checkMode !== CheckMode.Normal) {
8469+ return getTypeForVariableLikeDeclaration(node, /*includeOptionality*/ false, checkMode);
8470+ }
84658471 const symbol = getSymbolOfNode(node);
8466- return symbol && getSymbolLinks(symbol).type || getTypeForVariableLikeDeclaration(node, /*includeOptionality*/ false);
8472+ return symbol && getSymbolLinks(symbol).type || getTypeForVariableLikeDeclaration(node, /*includeOptionality*/ false, checkMode );
84678473 }
84688474
84698475 function getRestType(source: Type, properties: PropertyName[], symbol: Symbol | undefined): Type {
@@ -8594,7 +8600,8 @@ namespace ts {
85948600
85958601 /** Return the inferred type for a binding element */
85968602 function getTypeForBindingElement(declaration: BindingElement): Type | undefined {
8597- const parentType = getTypeForBindingElementParent(declaration.parent.parent);
8603+ const checkMode = declaration.dotDotDotToken ? CheckMode.RestBindingElement : CheckMode.Normal;
8604+ const parentType = getTypeForBindingElementParent(declaration.parent.parent, checkMode);
85988605 return parentType && getBindingElementTypeFromParentType(declaration, parentType);
85998606 }
86008607
@@ -8667,9 +8674,9 @@ namespace ts {
86678674 if (getEffectiveTypeAnnotationNode(walkUpBindingElementsAndPatterns(declaration))) {
86688675 // In strict null checking mode, if a default value of a non-undefined type is specified, remove
86698676 // undefined from the final type.
8670- return strictNullChecks && !(getFalsyFlags(checkDeclarationInitializer(declaration)) & TypeFlags.Undefined) ? getNonUndefinedType(type) : type;
8677+ return strictNullChecks && !(getFalsyFlags(checkDeclarationInitializer(declaration, CheckMode.Normal )) & TypeFlags.Undefined) ? getNonUndefinedType(type) : type;
86718678 }
8672- return widenTypeInferredFromInitializer(declaration, getUnionType([getNonUndefinedType(type), checkDeclarationInitializer(declaration)], UnionReduction.Subtype));
8679+ return widenTypeInferredFromInitializer(declaration, getUnionType([getNonUndefinedType(type), checkDeclarationInitializer(declaration, CheckMode.Normal )], UnionReduction.Subtype));
86738680 }
86748681
86758682 function getTypeForDeclarationFromJSDocComment(declaration: Node) {
@@ -8695,11 +8702,15 @@ namespace ts {
86958702 }
86968703
86978704 // Return the inferred type for a variable, parameter, or property declaration
8698- function getTypeForVariableLikeDeclaration(declaration: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement | JSDocPropertyLikeTag, includeOptionality: boolean): Type | undefined {
8705+ function getTypeForVariableLikeDeclaration(
8706+ declaration: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement | JSDocPropertyLikeTag,
8707+ includeOptionality: boolean,
8708+ checkMode: CheckMode,
8709+ ): Type | undefined {
86998710 // A variable declared in a for..in statement is of type string, or of type keyof T when the
87008711 // right hand expression is of a type parameter type.
87018712 if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === SyntaxKind.ForInStatement) {
8702- const indexType = getIndexType(getNonNullableTypeIfNeeded(checkExpression(declaration.parent.parent.expression)));
8713+ const indexType = getIndexType(getNonNullableTypeIfNeeded(checkExpression(declaration.parent.parent.expression, /*checkMode*/ checkMode )));
87038714 return indexType.flags & (TypeFlags.TypeParameter | TypeFlags.Index) ? getExtractStringType(indexType) : stringType;
87048715 }
87058716
@@ -8784,7 +8795,7 @@ namespace ts {
87848795 return containerObjectType;
87858796 }
87868797 }
8787- const type = widenTypeInferredFromInitializer(declaration, checkDeclarationInitializer(declaration));
8798+ const type = widenTypeInferredFromInitializer(declaration, checkDeclarationInitializer(declaration, checkMode ));
87888799 return addOptionality(type, isProperty, isOptional);
87898800 }
87908801
@@ -9177,7 +9188,7 @@ namespace ts {
91779188 // contextual type or, if the element itself is a binding pattern, with the type implied by that binding
91789189 // pattern.
91799190 const contextualType = isBindingPattern(element.name) ? getTypeFromBindingPattern(element.name, /*includePatternInType*/ true, /*reportErrors*/ false) : unknownType;
9180- return addOptionality(widenTypeInferredFromInitializer(element, checkDeclarationInitializer(element, contextualType)));
9191+ return addOptionality(widenTypeInferredFromInitializer(element, checkDeclarationInitializer(element, CheckMode.Normal, contextualType)));
91819192 }
91829193 if (isBindingPattern(element.name)) {
91839194 return getTypeFromBindingPattern(element.name, includePatternInType, reportErrors);
@@ -9269,7 +9280,7 @@ namespace ts {
92699280 // binding pattern [x, s = ""]. Because the contextual type is a tuple type, the resulting type of [1, "one"] is the
92709281 // tuple type [number, string]. Thus, the type inferred for 'x' is number and the type inferred for 's' is string.
92719282 function getWidenedTypeForVariableLikeDeclaration(declaration: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement | JSDocPropertyLikeTag, reportErrors?: boolean): Type {
9272- return widenTypeForVariableLikeDeclaration(getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true), declaration, reportErrors);
9283+ return widenTypeForVariableLikeDeclaration(getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true, CheckMode.Normal ), declaration, reportErrors);
92739284 }
92749285
92759286 function isGlobalSymbolConstructor(node: Node) {
@@ -25019,12 +25030,16 @@ namespace ts {
2501925030 return !!(type.flags & TypeFlags.Instantiable && !maybeTypeOfKind(getBaseConstraintOrType(type), TypeFlags.Nullable));
2502025031 }
2502125032
25022- function hasNonBindingPatternContextualTypeWithNoGenericTypes (node: Node) {
25033+ function hasContextualTypeWithNoGenericTypes (node: Node, checkMode: CheckMode | undefined ) {
2502325034 // Computing the contextual type for a child of a JSX element involves resolving the type of the
2502425035 // element's tag name, so we exclude that here to avoid circularities.
25036+ // If check mode has `CheckMode.RestBindingElement`, we skip binding pattern contextual types,
25037+ // as we want the type of a rest element to be generic when possible.
2502525038 const contextualType = (isIdentifier(node) || isPropertyAccessExpression(node) || isElementAccessExpression(node)) &&
2502625039 !((isJsxOpeningElement(node.parent) || isJsxSelfClosingElement(node.parent)) && node.parent.tagName === node) &&
25027- getContextualType(node, ContextFlags.SkipBindingPatterns);
25040+ (checkMode && checkMode & CheckMode.RestBindingElement ?
25041+ getContextualType(node, ContextFlags.SkipBindingPatterns)
25042+ : getContextualType(node));
2502825043 return contextualType && !isGenericType(contextualType);
2502925044 }
2503025045
@@ -25038,7 +25053,7 @@ namespace ts {
2503825053 // 'string | undefined' to give control flow analysis the opportunity to narrow to type 'string'.
2503925054 const substituteConstraints = !(checkMode && checkMode & CheckMode.Inferential) &&
2504025055 someType(type, isGenericTypeWithUnionConstraint) &&
25041- (isConstraintPosition(type, reference) || hasNonBindingPatternContextualTypeWithNoGenericTypes (reference));
25056+ (isConstraintPosition(type, reference) || hasContextualTypeWithNoGenericTypes (reference, checkMode ));
2504225057 return substituteConstraints ? mapType(type, t => t.flags & TypeFlags.Instantiable && !isMappedTypeGenericIndexedAccess(t) ? getBaseConstraintOrType(t) : t) : type;
2504325058 }
2504425059
@@ -25110,7 +25125,7 @@ namespace ts {
2511025125 const links = getNodeLinks(location);
2511125126 if (!(links.flags & NodeCheckFlags.InCheckIdentifier)) {
2511225127 links.flags |= NodeCheckFlags.InCheckIdentifier;
25113- const parentType = getTypeForBindingElementParent(parent);
25128+ const parentType = getTypeForBindingElementParent(parent, CheckMode.Normal );
2511425129 links.flags &= ~NodeCheckFlags.InCheckIdentifier;
2511525130 if (parentType && parentType.flags & TypeFlags.Union && !(parent.kind === SyntaxKind.Parameter && isSymbolAssigned(symbol))) {
2511625131 const pattern = declaration.parent;
@@ -26047,7 +26062,7 @@ namespace ts {
2604726062 const parent = declaration.parent.parent;
2604826063 const name = declaration.propertyName || declaration.name;
2604926064 const parentType = getContextualTypeForVariableLikeDeclaration(parent) ||
26050- parent.kind !== SyntaxKind.BindingElement && parent.initializer && checkDeclarationInitializer(parent);
26065+ parent.kind !== SyntaxKind.BindingElement && parent.initializer && checkDeclarationInitializer(parent, declaration.dotDotDotToken ? CheckMode.RestBindingElement : CheckMode.Normal );
2605126066 if (!parentType || isBindingPattern(name) || isComputedNonLiteralName(name)) return undefined;
2605226067 if (parent.name.kind === SyntaxKind.ArrayBindingPattern) {
2605326068 const index = indexOfNode(declaration.parent.elements, declaration);
@@ -31807,7 +31822,7 @@ namespace ts {
3180731822 const links = getSymbolLinks(parameter);
3180831823 if (!links.type) {
3180931824 const declaration = parameter.valueDeclaration as ParameterDeclaration;
31810- links.type = type || getWidenedTypeForVariableLikeDeclaration(declaration, /*includeOptionality */ true);
31825+ links.type = type || getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors */ true);
3181131826 if (declaration.name.kind !== SyntaxKind.Identifier) {
3181231827 // if inference didn't come up with anything but unknown, fall back to the binding pattern if present.
3181331828 if (links.type === unknownType) {
@@ -33724,11 +33739,11 @@ namespace ts {
3372433739 }
3372533740
3372633741 function checkExpressionCached(node: Expression | QualifiedName, checkMode?: CheckMode): Type {
33742+ if (checkMode && checkMode !== CheckMode.Normal) {
33743+ return checkExpression(node, checkMode);
33744+ }
3372733745 const links = getNodeLinks(node);
3372833746 if (!links.resolvedType) {
33729- if (checkMode && checkMode !== CheckMode.Normal) {
33730- return checkExpression(node, checkMode);
33731- }
3373233747 // When computing a type that we're going to cache, we need to ignore any ongoing control flow
3373333748 // analysis because variables may have transient types in indeterminable states. Moving flowLoopStart
3373433749 // to the top of the stack ensures all transient types are computed from a known point.
@@ -33750,10 +33765,16 @@ namespace ts {
3375033765 isJSDocTypeAssertion(node);
3375133766 }
3375233767
33753- function checkDeclarationInitializer(declaration: HasExpressionInitializer, contextualType?: Type | undefined) {
33768+ function checkDeclarationInitializer(
33769+ declaration: HasExpressionInitializer,
33770+ checkMode: CheckMode,
33771+ contextualType?: Type | undefined
33772+ ) {
3375433773 const initializer = getEffectiveInitializer(declaration)!;
3375533774 const type = getQuickTypeOfExpression(initializer) ||
33756- (contextualType ? checkExpressionWithContextualType(initializer, contextualType, /*inferenceContext*/ undefined, CheckMode.Normal) : checkExpressionCached(initializer));
33775+ (contextualType ?
33776+ checkExpressionWithContextualType(initializer, contextualType, /*inferenceContext*/ undefined, checkMode || CheckMode.Normal)
33777+ : checkExpressionCached(initializer, checkMode));
3375733778 return isParameter(declaration) && declaration.name.kind === SyntaxKind.ArrayBindingPattern &&
3375833779 isTupleType(type) && !type.target.hasRestElement && getTypeReferenceArity(type) < declaration.name.elements.length ?
3375933780 padTupleType(type, declaration.name) : type;
@@ -37011,7 +37032,8 @@ namespace ts {
3701137032
3701237033 // check private/protected variable access
3701337034 const parent = node.parent.parent;
37014- const parentType = getTypeForBindingElementParent(parent);
37035+ const parentCheckMode = node.dotDotDotToken ? CheckMode.RestBindingElement : CheckMode.Normal;
37036+ const parentType = getTypeForBindingElementParent(parent, parentCheckMode);
3701537037 const name = node.propertyName || node.name;
3701637038 if (parentType && !isBindingPattern(name)) {
3701737039 const exprType = getLiteralTypeFromPropertyName(name);
@@ -38409,7 +38431,7 @@ namespace ts {
3840938431 const declaration = catchClause.variableDeclaration;
3841038432 const typeNode = getEffectiveTypeAnnotationNode(getRootDeclaration(declaration));
3841138433 if (typeNode) {
38412- const type = getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ false);
38434+ const type = getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ false, CheckMode.Normal );
3841338435 if (type && !(type.flags & TypeFlags.AnyOrUnknown)) {
3841438436 grammarErrorOnFirstToken(typeNode, Diagnostics.Catch_clause_variable_type_annotation_must_be_any_or_unknown_if_specified);
3841538437 }
@@ -41440,7 +41462,7 @@ namespace ts {
4144041462 }
4144141463
4144241464 if (isBindingPattern(node)) {
41443- return getTypeForVariableLikeDeclaration(node.parent, /*includeOptionality*/ true) || errorType;
41465+ return getTypeForVariableLikeDeclaration(node.parent, /*includeOptionality*/ true, CheckMode.Normal ) || errorType;
4144441466 }
4144541467
4144641468 if (isInRightSideOfImportOrExportAssignment(node as Identifier)) {
0 commit comments