Fixed a false positive related to binding patterns and spread expressions (#49684)

* Fixed a false positive related to binding patterns and spread expressions

* Improve ancestor lookup when checking if an expression is spread into an object

* Fixed ancestor lookup for more node types

* Remove equality check for contextual types

* Reformat code

* Use `isWithinSpreadAssignment` flag + `objectsWithinSpread` cache instead of ancestor traversal

* Revert "Use `isWithinSpreadAssignment` flag + `objectsWithinSpread` cache instead of ancestor traversal"

This reverts commit be387e3bbf8a5cce2bc4c31fd77b061ea6cf8e0b.

* Expand on the existing comment
This commit is contained in:
Mateusz Burzyński 2022-08-09 02:01:50 +02:00 committed by GitHub
parent 71e8529228
commit 0e17dc7769
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 107 additions and 9 deletions

View File

@ -28163,16 +28163,31 @@ namespace ts {
// If object literal is contextually typed by the implied type of a binding pattern, augment the result
// type with those properties for which the binding pattern specifies a default value.
// If the object literal is spread into another object literal, skip this step and let the top-level object
// literal handle it instead.
if (contextualTypeHasPattern && node.parent.kind !== SyntaxKind.SpreadAssignment) {
for (const prop of getPropertiesOfType(contextualType)) {
if (!propertiesTable.get(prop.escapedName) && !getPropertyOfType(spread, prop.escapedName)) {
if (!(prop.flags & SymbolFlags.Optional)) {
error(prop.valueDeclaration || (prop as TransientSymbol).bindingElement,
Diagnostics.Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value);
// literal handle it instead. Note that this might require full traversal to the root pattern's parent
// as it's the guaranteed to be the common ancestor of the pattern node and the current object node.
// It's not possible to check if the immediate parent node is a spread assignment
// since the type flows in non-obvious ways through conditional expressions, IIFEs and more.
if (contextualTypeHasPattern) {
const rootPatternParent = findAncestor(contextualType.pattern!.parent, n =>
n.kind === SyntaxKind.VariableDeclaration ||
n.kind === SyntaxKind.BinaryExpression ||
n.kind === SyntaxKind.Parameter
);
const spreadOrOutsideRootObject = findAncestor(node, n =>
n === rootPatternParent ||
n.kind === SyntaxKind.SpreadAssignment
)!;
if (spreadOrOutsideRootObject.kind !== SyntaxKind.SpreadAssignment) {
for (const prop of getPropertiesOfType(contextualType)) {
if (!propertiesTable.get(prop.escapedName) && !getPropertyOfType(spread, prop.escapedName)) {
if (!(prop.flags & SymbolFlags.Optional)) {
error(prop.valueDeclaration || (prop as TransientSymbol).bindingElement,
Diagnostics.Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value);
}
propertiesTable.set(prop.escapedName, prop);
propertiesArray.push(prop);
}
propertiesTable.set(prop.escapedName, prop);
propertiesArray.push(prop);
}
}
}

View File

@ -0,0 +1,23 @@
=== tests/cases/compiler/spreadExpressionContainingObjectExpressionContextualType.ts ===
// repro #49585
const { value } = (() => ({
>value : Symbol(value, Decl(spreadExpressionContainingObjectExpressionContextualType.ts, 2, 7))
value: "",
>value : Symbol(value, Decl(spreadExpressionContainingObjectExpressionContextualType.ts, 2, 27))
...(true ? {} : {}),
}))();
// repro 49684#discussion_r920545763
const { value2 } = {
>value2 : Symbol(value2, Decl(spreadExpressionContainingObjectExpressionContextualType.ts, 9, 7))
value2: "",
>value2 : Symbol(value2, Decl(spreadExpressionContainingObjectExpressionContextualType.ts, 9, 20))
...(() => true ? {} : {})(),
};

View File

@ -0,0 +1,45 @@
=== tests/cases/compiler/spreadExpressionContainingObjectExpressionContextualType.ts ===
// repro #49585
const { value } = (() => ({
>value : string
>(() => ({ value: "", ...(true ? {} : {}),}))() : { value: string; }
>(() => ({ value: "", ...(true ? {} : {}),})) : () => { value: string; }
>() => ({ value: "", ...(true ? {} : {}),}) : () => { value: string; }
>({ value: "", ...(true ? {} : {}),}) : { value: string; }
>{ value: "", ...(true ? {} : {}),} : { value: string; }
value: "",
>value : string
>"" : ""
...(true ? {} : {}),
>(true ? {} : {}) : {}
>true ? {} : {} : {}
>true : true
>{} : {}
>{} : {}
}))();
// repro 49684#discussion_r920545763
const { value2 } = {
>value2 : string
>{ value2: "", ...(() => true ? {} : {})(),} : { value2: string; }
value2: "",
>value2 : string
>"" : ""
...(() => true ? {} : {})(),
>(() => true ? {} : {})() : {}
>(() => true ? {} : {}) : () => {}
>() => true ? {} : {} : () => {}
>true ? {} : {} : {}
>true : true
>{} : {}
>{} : {}
};

View File

@ -0,0 +1,15 @@
// @noEmit: true
// repro #49585
const { value } = (() => ({
value: "",
...(true ? {} : {}),
}))();
// repro 49684#discussion_r920545763
const { value2 } = {
value2: "",
...(() => true ? {} : {})(),
};