@@ -175,12 +175,15 @@ namespace ts {
175
175
}
176
176
177
177
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
184
187
}
185
188
186
189
const enum SignatureCheckMode {
@@ -8461,9 +8464,12 @@ namespace ts {
8461
8464
8462
8465
// Return the type of a binding element parent. We check SymbolLinks first to see if a type has been
8463
8466
// 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
+ }
8465
8471
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 );
8467
8473
}
8468
8474
8469
8475
function getRestType(source: Type, properties: PropertyName[], symbol: Symbol | undefined): Type {
@@ -8594,7 +8600,8 @@ namespace ts {
8594
8600
8595
8601
/** Return the inferred type for a binding element */
8596
8602
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);
8598
8605
return parentType && getBindingElementTypeFromParentType(declaration, parentType);
8599
8606
}
8600
8607
@@ -8667,9 +8674,9 @@ namespace ts {
8667
8674
if (getEffectiveTypeAnnotationNode(walkUpBindingElementsAndPatterns(declaration))) {
8668
8675
// In strict null checking mode, if a default value of a non-undefined type is specified, remove
8669
8676
// 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;
8671
8678
}
8672
- return widenTypeInferredFromInitializer(declaration, getUnionType([getNonUndefinedType(type), checkDeclarationInitializer(declaration)], UnionReduction.Subtype));
8679
+ return widenTypeInferredFromInitializer(declaration, getUnionType([getNonUndefinedType(type), checkDeclarationInitializer(declaration, CheckMode.Normal )], UnionReduction.Subtype));
8673
8680
}
8674
8681
8675
8682
function getTypeForDeclarationFromJSDocComment(declaration: Node) {
@@ -8695,11 +8702,15 @@ namespace ts {
8695
8702
}
8696
8703
8697
8704
// 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 {
8699
8710
// A variable declared in a for..in statement is of type string, or of type keyof T when the
8700
8711
// right hand expression is of a type parameter type.
8701
8712
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 )));
8703
8714
return indexType.flags & (TypeFlags.TypeParameter | TypeFlags.Index) ? getExtractStringType(indexType) : stringType;
8704
8715
}
8705
8716
@@ -8784,7 +8795,7 @@ namespace ts {
8784
8795
return containerObjectType;
8785
8796
}
8786
8797
}
8787
- const type = widenTypeInferredFromInitializer(declaration, checkDeclarationInitializer(declaration));
8798
+ const type = widenTypeInferredFromInitializer(declaration, checkDeclarationInitializer(declaration, checkMode ));
8788
8799
return addOptionality(type, isProperty, isOptional);
8789
8800
}
8790
8801
@@ -9177,7 +9188,7 @@ namespace ts {
9177
9188
// contextual type or, if the element itself is a binding pattern, with the type implied by that binding
9178
9189
// pattern.
9179
9190
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)));
9181
9192
}
9182
9193
if (isBindingPattern(element.name)) {
9183
9194
return getTypeFromBindingPattern(element.name, includePatternInType, reportErrors);
@@ -9269,7 +9280,7 @@ namespace ts {
9269
9280
// binding pattern [x, s = ""]. Because the contextual type is a tuple type, the resulting type of [1, "one"] is the
9270
9281
// tuple type [number, string]. Thus, the type inferred for 'x' is number and the type inferred for 's' is string.
9271
9282
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);
9273
9284
}
9274
9285
9275
9286
function isGlobalSymbolConstructor(node: Node) {
@@ -25019,12 +25030,16 @@ namespace ts {
25019
25030
return !!(type.flags & TypeFlags.Instantiable && !maybeTypeOfKind(getBaseConstraintOrType(type), TypeFlags.Nullable));
25020
25031
}
25021
25032
25022
- function hasNonBindingPatternContextualTypeWithNoGenericTypes (node: Node) {
25033
+ function hasContextualTypeWithNoGenericTypes (node: Node, checkMode: CheckMode | undefined ) {
25023
25034
// Computing the contextual type for a child of a JSX element involves resolving the type of the
25024
25035
// 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.
25025
25038
const contextualType = (isIdentifier(node) || isPropertyAccessExpression(node) || isElementAccessExpression(node)) &&
25026
25039
!((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));
25028
25043
return contextualType && !isGenericType(contextualType);
25029
25044
}
25030
25045
@@ -25038,7 +25053,7 @@ namespace ts {
25038
25053
// 'string | undefined' to give control flow analysis the opportunity to narrow to type 'string'.
25039
25054
const substituteConstraints = !(checkMode && checkMode & CheckMode.Inferential) &&
25040
25055
someType(type, isGenericTypeWithUnionConstraint) &&
25041
- (isConstraintPosition(type, reference) || hasNonBindingPatternContextualTypeWithNoGenericTypes (reference));
25056
+ (isConstraintPosition(type, reference) || hasContextualTypeWithNoGenericTypes (reference, checkMode ));
25042
25057
return substituteConstraints ? mapType(type, t => t.flags & TypeFlags.Instantiable && !isMappedTypeGenericIndexedAccess(t) ? getBaseConstraintOrType(t) : t) : type;
25043
25058
}
25044
25059
@@ -25110,7 +25125,7 @@ namespace ts {
25110
25125
const links = getNodeLinks(location);
25111
25126
if (!(links.flags & NodeCheckFlags.InCheckIdentifier)) {
25112
25127
links.flags |= NodeCheckFlags.InCheckIdentifier;
25113
- const parentType = getTypeForBindingElementParent(parent);
25128
+ const parentType = getTypeForBindingElementParent(parent, CheckMode.Normal );
25114
25129
links.flags &= ~NodeCheckFlags.InCheckIdentifier;
25115
25130
if (parentType && parentType.flags & TypeFlags.Union && !(parent.kind === SyntaxKind.Parameter && isSymbolAssigned(symbol))) {
25116
25131
const pattern = declaration.parent;
@@ -26047,7 +26062,7 @@ namespace ts {
26047
26062
const parent = declaration.parent.parent;
26048
26063
const name = declaration.propertyName || declaration.name;
26049
26064
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 );
26051
26066
if (!parentType || isBindingPattern(name) || isComputedNonLiteralName(name)) return undefined;
26052
26067
if (parent.name.kind === SyntaxKind.ArrayBindingPattern) {
26053
26068
const index = indexOfNode(declaration.parent.elements, declaration);
@@ -31807,7 +31822,7 @@ namespace ts {
31807
31822
const links = getSymbolLinks(parameter);
31808
31823
if (!links.type) {
31809
31824
const declaration = parameter.valueDeclaration as ParameterDeclaration;
31810
- links.type = type || getWidenedTypeForVariableLikeDeclaration(declaration, /*includeOptionality */ true);
31825
+ links.type = type || getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors */ true);
31811
31826
if (declaration.name.kind !== SyntaxKind.Identifier) {
31812
31827
// if inference didn't come up with anything but unknown, fall back to the binding pattern if present.
31813
31828
if (links.type === unknownType) {
@@ -33724,11 +33739,11 @@ namespace ts {
33724
33739
}
33725
33740
33726
33741
function checkExpressionCached(node: Expression | QualifiedName, checkMode?: CheckMode): Type {
33742
+ if (checkMode && checkMode !== CheckMode.Normal) {
33743
+ return checkExpression(node, checkMode);
33744
+ }
33727
33745
const links = getNodeLinks(node);
33728
33746
if (!links.resolvedType) {
33729
- if (checkMode && checkMode !== CheckMode.Normal) {
33730
- return checkExpression(node, checkMode);
33731
- }
33732
33747
// When computing a type that we're going to cache, we need to ignore any ongoing control flow
33733
33748
// analysis because variables may have transient types in indeterminable states. Moving flowLoopStart
33734
33749
// to the top of the stack ensures all transient types are computed from a known point.
@@ -33750,10 +33765,16 @@ namespace ts {
33750
33765
isJSDocTypeAssertion(node);
33751
33766
}
33752
33767
33753
- function checkDeclarationInitializer(declaration: HasExpressionInitializer, contextualType?: Type | undefined) {
33768
+ function checkDeclarationInitializer(
33769
+ declaration: HasExpressionInitializer,
33770
+ checkMode: CheckMode,
33771
+ contextualType?: Type | undefined
33772
+ ) {
33754
33773
const initializer = getEffectiveInitializer(declaration)!;
33755
33774
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));
33757
33778
return isParameter(declaration) && declaration.name.kind === SyntaxKind.ArrayBindingPattern &&
33758
33779
isTupleType(type) && !type.target.hasRestElement && getTypeReferenceArity(type) < declaration.name.elements.length ?
33759
33780
padTupleType(type, declaration.name) : type;
@@ -37011,7 +37032,8 @@ namespace ts {
37011
37032
37012
37033
// check private/protected variable access
37013
37034
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);
37015
37037
const name = node.propertyName || node.name;
37016
37038
if (parentType && !isBindingPattern(name)) {
37017
37039
const exprType = getLiteralTypeFromPropertyName(name);
@@ -38409,7 +38431,7 @@ namespace ts {
38409
38431
const declaration = catchClause.variableDeclaration;
38410
38432
const typeNode = getEffectiveTypeAnnotationNode(getRootDeclaration(declaration));
38411
38433
if (typeNode) {
38412
- const type = getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ false);
38434
+ const type = getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ false, CheckMode.Normal );
38413
38435
if (type && !(type.flags & TypeFlags.AnyOrUnknown)) {
38414
38436
grammarErrorOnFirstToken(typeNode, Diagnostics.Catch_clause_variable_type_annotation_must_be_any_or_unknown_if_specified);
38415
38437
}
@@ -41440,7 +41462,7 @@ namespace ts {
41440
41462
}
41441
41463
41442
41464
if (isBindingPattern(node)) {
41443
- return getTypeForVariableLikeDeclaration(node.parent, /*includeOptionality*/ true) || errorType;
41465
+ return getTypeForVariableLikeDeclaration(node.parent, /*includeOptionality*/ true, CheckMode.Normal ) || errorType;
41444
41466
}
41445
41467
41446
41468
if (isInRightSideOfImportOrExportAssignment(node as Identifier)) {
0 commit comments