You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
🔨 Limit binding element/pattern type inference to literals (optional)
Signed-off-by: Babak K. Shandiz <[email protected]>
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index abf3e9d..21eefd8 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -9440,16 +9440,32 @@ namespace ts {
// Return the type implied by a binding pattern element. This is the type of the initializer of the element if
// one is present. Otherwise, if the element is itself a binding pattern, it is the type implied by the binding
// pattern. Otherwise, it is the type any.
- function getTypeFromBindingElement(element: BindingElement, includePatternInType?: boolean, reportErrors?: boolean): Type {
+ function getTypeFromBindingElement(element: BindingElement, includePatternInType?: boolean, reportErrors?: boolean, skipNonLiteralInitializer?: boolean): Type {
if (element.initializer) {
// The type implied by a binding pattern is independent of context, so we check the initializer with no
// contextual type or, if the element itself is a binding pattern, with the type implied by that binding
// pattern.
- const contextualType = isBindingPattern(element.name) ? getTypeFromBindingPattern(element.name, /*includePatternInType*/ true, /*reportErrors*/ false) : unknownType;
- return addOptionality(widenTypeInferredFromInitializer(element, checkDeclarationInitializer(element, CheckMode.Normal, contextualType)));
+ const contextualType = isBindingPattern(element.name)
+ ? getTypeFromBindingPattern(element.name, /*includePatternInType*/ true, /*reportErrors*/ false, skipNonLiteralInitializer)
+ : unknownType;
+
+ // (microsoft#49989)
+ // If `skipNonLiteralInitializer` is not set, in cases where the intitializer is an identifier pointing
+ // to a sibling symbol in the same declaration, a false circular relationship will be concluded. For
+ // example, take the declarations below:
+ //
+ // const [a, b = a] = [1];
+ // const {a, b = a} = {a: 1};
+ //
+ // Here when the `element` is the second binding element, `b = a`, the initializer is `a` which itself
+ // is defined within the same binding pattern.
+ const type = skipNonLiteralInitializer && !isLiteralExpression(element.initializer)
+ ? contextualType
+ : checkDeclarationInitializer(element, CheckMode.Normal, contextualType);
+ return addOptionality(widenTypeInferredFromInitializer(element, type));
}
if (isBindingPattern(element.name)) {
- return getTypeFromBindingPattern(element.name, includePatternInType, reportErrors);
+ return getTypeFromBindingPattern(element.name, includePatternInType, reportErrors, skipNonLiteralInitializer);
}
if (reportErrors && !declarationBelongsToPrivateAmbientMember(element)) {
reportImplicitAny(element, anyType);
@@ -9458,7 +9474,7 @@ namespace ts {
}
// Return the type implied by an object binding pattern
- function getTypeFromObjectBindingPattern(pattern: ObjectBindingPattern, includePatternInType: boolean, reportErrors: boolean): Type {
+ function getTypeFromObjectBindingPattern(pattern: ObjectBindingPattern, includePatternInType: boolean, reportErrors: boolean, skipNonLiteralInitializer: boolean): Type {
const members = createSymbolTable();
let stringIndexInfo: IndexInfo | undefined;
let objectFlags = ObjectFlags.ObjectLiteral | ObjectFlags.ContainsObjectOrArrayLiteral;
@@ -9478,7 +9494,7 @@ namespace ts {
const text = getPropertyNameFromType(exprType);
const flags = SymbolFlags.Property | (e.initializer ? SymbolFlags.Optional : 0);
const symbol = createSymbol(flags, text);
- symbol.type = getTypeFromBindingElement(e, includePatternInType, reportErrors);
+ symbol.type = getTypeFromBindingElement(e, includePatternInType, reportErrors, skipNonLiteralInitializer);
symbol.bindingElement = e;
members.set(symbol.escapedName, symbol);
});
@@ -9492,14 +9508,14 @@ namespace ts {
}
// Return the type implied by an array binding pattern
- function getTypeFromArrayBindingPattern(pattern: BindingPattern, includePatternInType: boolean, reportErrors: boolean): Type {
+ function getTypeFromArrayBindingPattern(pattern: BindingPattern, includePatternInType: boolean, reportErrors: boolean, skipNonLiteralInitializer: boolean): Type {
const elements = pattern.elements;
const lastElement = lastOrUndefined(elements);
const restElement = lastElement && lastElement.kind === SyntaxKind.BindingElement && lastElement.dotDotDotToken ? lastElement : undefined;
if (elements.length === 0 || elements.length === 1 && restElement) {
return languageVersion >= ScriptTarget.ES2015 ? createIterableType(anyType) : anyArrayType;
}
- const elementTypes = map(elements, e => isOmittedExpression(e) ? anyType : getTypeFromBindingElement(e, includePatternInType, reportErrors));
+ const elementTypes = map(elements, e => isOmittedExpression(e) ? anyType : getTypeFromBindingElement(e, includePatternInType, reportErrors, skipNonLiteralInitializer));
const minLength = findLastIndex(elements, e => !(e === restElement || isOmittedExpression(e) || hasDefaultValue(e)), elements.length - 1) + 1;
const elementFlags = map(elements, (e, i) => e === restElement ? ElementFlags.Rest : i >= minLength ? ElementFlags.Optional : ElementFlags.Required);
let result = createTupleType(elementTypes, elementFlags) as TypeReference;
@@ -9518,10 +9534,10 @@ namespace ts {
// used as the contextual type of an initializer associated with the binding pattern. Also, for a destructuring
// parameter with no type annotation or initializer, the type implied by the binding pattern becomes the type of
// the parameter.
- function getTypeFromBindingPattern(pattern: BindingPattern, includePatternInType = false, reportErrors = false): Type {
+ function getTypeFromBindingPattern(pattern: BindingPattern, includePatternInType = false, reportErrors = false, skipNonLiteralInitializer = false): Type {
return pattern.kind === SyntaxKind.ObjectBindingPattern
- ? getTypeFromObjectBindingPattern(pattern, includePatternInType, reportErrors)
- : getTypeFromArrayBindingPattern(pattern, includePatternInType, reportErrors);
+ ? getTypeFromObjectBindingPattern(pattern, includePatternInType, reportErrors, skipNonLiteralInitializer)
+ : getTypeFromArrayBindingPattern(pattern, includePatternInType, reportErrors, skipNonLiteralInitializer);
}
// Return the type associated with a variable, parameter, or property declaration. In the simple case this is the type
@@ -26753,7 +26769,7 @@ namespace ts {
return result;
}
if (!(contextFlags! & ContextFlags.SkipBindingPatterns) && isBindingPattern(declaration.name) && declaration.name.elements.length > 0) {
- return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ true, /*reportErrors*/ false);
+ return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ true, /*reportErrors*/ false, /*skipNonLiteralInitializer*/ true);
}
}
return undefined;
0 commit comments