@@ -1529,6 +1529,63 @@ namespace ts {
1529
1529
}
1530
1530
}
1531
1531
1532
+ function useOuterVariableScopeInParameter(result: Symbol, location: Node, lastLocation: Node) {
1533
+ const target = getEmitScriptTarget(compilerOptions);
1534
+ const functionLocation = <FunctionLikeDeclaration>location;
1535
+ if (isParameter(lastLocation) && functionLocation.body && result.valueDeclaration.pos >= functionLocation.body.pos && result.valueDeclaration.end <= functionLocation.body.end) {
1536
+ // check for several cases where we introduce temporaries that require moving the name/initializer of the parameter to the body
1537
+ // - static field in a class expression
1538
+ // - optional chaining pre-es2020
1539
+ // - nullish coalesce pre-es2020
1540
+ // - spread assignment in binding pattern pre-es2017
1541
+ if (target >= ScriptTarget.ES2015) {
1542
+ const links = getNodeLinks(functionLocation);
1543
+ if (links.declarationRequiresScopeChange === undefined) {
1544
+ links.declarationRequiresScopeChange = forEach(functionLocation.parameters, requiresScopeChange) || false;
1545
+ }
1546
+ return !links.declarationRequiresScopeChange;
1547
+ }
1548
+ }
1549
+ return false;
1550
+
1551
+ function requiresScopeChange(node: ParameterDeclaration): boolean {
1552
+ return requiresScopeChangeWorker(node.name)
1553
+ || !!node.initializer && requiresScopeChangeWorker(node.initializer);
1554
+ }
1555
+
1556
+ function requiresScopeChangeWorker(node: Node): boolean {
1557
+ switch (node.kind) {
1558
+ case SyntaxKind.ArrowFunction:
1559
+ case SyntaxKind.FunctionExpression:
1560
+ case SyntaxKind.FunctionDeclaration:
1561
+ case SyntaxKind.Constructor:
1562
+ // do not descend into these
1563
+ return false;
1564
+ case SyntaxKind.MethodDeclaration:
1565
+ case SyntaxKind.GetAccessor:
1566
+ case SyntaxKind.SetAccessor:
1567
+ case SyntaxKind.PropertyAssignment:
1568
+ return requiresScopeChangeWorker((node as MethodDeclaration | AccessorDeclaration | PropertyAssignment).name);
1569
+ case SyntaxKind.PropertyDeclaration:
1570
+ // static properties in classes introduce temporary variables
1571
+ if (hasStaticModifier(node)) {
1572
+ return target < ScriptTarget.ESNext || !compilerOptions.useDefineForClassFields;
1573
+ }
1574
+ return requiresScopeChangeWorker((node as PropertyDeclaration).name);
1575
+ default:
1576
+ // null coalesce and optional chain pre-es2020 produce temporary variables
1577
+ if (isNullishCoalesce(node) || isOptionalChain(node)) {
1578
+ return target < ScriptTarget.ES2020;
1579
+ }
1580
+ if (isBindingElement(node) && node.dotDotDotToken && isObjectBindingPattern(node.parent)) {
1581
+ return target < ScriptTarget.ES2017;
1582
+ }
1583
+ if (isTypeNode(node)) return false;
1584
+ return forEachChild(node, requiresScopeChangeWorker) || false;
1585
+ }
1586
+ }
1587
+ }
1588
+
1532
1589
/**
1533
1590
* Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and
1534
1591
* the nameNotFoundMessage argument is not undefined. Returns the resolved symbol, or undefined if no symbol with
@@ -1563,7 +1620,7 @@ namespace ts {
1563
1620
let lastLocation: Node | undefined;
1564
1621
let lastSelfReferenceLocation: Node | undefined;
1565
1622
let propertyWithInvalidInitializer: Node | undefined;
1566
- let associatedDeclarationForContainingInitializer : ParameterDeclaration | BindingElement | undefined;
1623
+ let associatedDeclarationForContainingInitializerOrBindingName : ParameterDeclaration | BindingElement | undefined;
1567
1624
let withinDeferredContext = false;
1568
1625
const errorLocation = location;
1569
1626
let grandparent: Node;
@@ -1592,9 +1649,7 @@ namespace ts {
1592
1649
}
1593
1650
if (meaning & result.flags & SymbolFlags.Variable) {
1594
1651
// expression inside parameter will lookup as normal variable scope when targeting es2015+
1595
- const functionLocation = <FunctionLikeDeclaration>location;
1596
- if (compilerOptions.target && compilerOptions.target >= ScriptTarget.ES2015 && isParameter(lastLocation) &&
1597
- functionLocation.body && result.valueDeclaration.pos >= functionLocation.body.pos && result.valueDeclaration.end <= functionLocation.body.end) {
1652
+ if (useOuterVariableScopeInParameter(result, location, lastLocation)) {
1598
1653
useResult = false;
1599
1654
}
1600
1655
else if (result.flags & SymbolFlags.FunctionScopedVariable) {
@@ -1822,15 +1877,21 @@ namespace ts {
1822
1877
location = getJSDocHost(location);
1823
1878
break;
1824
1879
case SyntaxKind.Parameter:
1825
- if (lastLocation && lastLocation === (location as ParameterDeclaration).initializer) {
1826
- associatedDeclarationForContainingInitializer = location as ParameterDeclaration;
1880
+ if (lastLocation && (
1881
+ lastLocation === (location as ParameterDeclaration).initializer ||
1882
+ lastLocation === (location as ParameterDeclaration).name && isBindingPattern(lastLocation))) {
1883
+ if (!associatedDeclarationForContainingInitializerOrBindingName) {
1884
+ associatedDeclarationForContainingInitializerOrBindingName = location as ParameterDeclaration;
1885
+ }
1827
1886
}
1828
1887
break;
1829
1888
case SyntaxKind.BindingElement:
1830
- if (lastLocation && lastLocation === (location as BindingElement).initializer) {
1889
+ if (lastLocation && (
1890
+ lastLocation === (location as BindingElement).initializer ||
1891
+ lastLocation === (location as BindingElement).name && isBindingPattern(lastLocation))) {
1831
1892
const root = getRootDeclaration(location);
1832
1893
if (root.kind === SyntaxKind.Parameter) {
1833
- associatedDeclarationForContainingInitializer = location as BindingElement;
1894
+ associatedDeclarationForContainingInitializerOrBindingName = location as BindingElement;
1834
1895
}
1835
1896
}
1836
1897
break;
@@ -1941,17 +2002,17 @@ namespace ts {
1941
2002
}
1942
2003
}
1943
2004
1944
- // If we're in a parameter initializer, we can't reference the values of the parameter whose initializer we're within or parameters to the right
1945
- if (result && associatedDeclarationForContainingInitializer && !withinDeferredContext && (meaning & SymbolFlags.Value) === SymbolFlags.Value) {
2005
+ // If we're in a parameter initializer or binding name , we can't reference the values of the parameter whose initializer we're within or parameters to the right
2006
+ if (result && associatedDeclarationForContainingInitializerOrBindingName && !withinDeferredContext && (meaning & SymbolFlags.Value) === SymbolFlags.Value) {
1946
2007
const candidate = getMergedSymbol(getLateBoundSymbol(result));
1947
- const root = (getRootDeclaration(associatedDeclarationForContainingInitializer ) as ParameterDeclaration);
2008
+ const root = (getRootDeclaration(associatedDeclarationForContainingInitializerOrBindingName ) as ParameterDeclaration);
1948
2009
// A parameter initializer or binding pattern initializer within a parameter cannot refer to itself
1949
- if (candidate === getSymbolOfNode(associatedDeclarationForContainingInitializer )) {
1950
- error(errorLocation, Diagnostics.Parameter_0_cannot_be_referenced_in_its_initializer , declarationNameToString(associatedDeclarationForContainingInitializer .name));
2010
+ if (candidate === getSymbolOfNode(associatedDeclarationForContainingInitializerOrBindingName )) {
2011
+ error(errorLocation, Diagnostics.Parameter_0_cannot_reference_itself , declarationNameToString(associatedDeclarationForContainingInitializerOrBindingName .name));
1951
2012
}
1952
2013
// And it cannot refer to any declarations which come after it
1953
- else if (candidate.valueDeclaration && candidate.valueDeclaration.pos > associatedDeclarationForContainingInitializer .pos && root.parent.locals && lookup(root.parent.locals, candidate.escapedName, meaning) === candidate) {
1954
- error(errorLocation, Diagnostics.Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it , declarationNameToString(associatedDeclarationForContainingInitializer .name), declarationNameToString(<Identifier>errorLocation));
2014
+ else if (candidate.valueDeclaration && candidate.valueDeclaration.pos > associatedDeclarationForContainingInitializerOrBindingName .pos && root.parent.locals && lookup(root.parent.locals, candidate.escapedName, meaning) === candidate) {
2015
+ error(errorLocation, Diagnostics.Parameter_0_cannot_reference_identifier_1_declared_after_it , declarationNameToString(associatedDeclarationForContainingInitializerOrBindingName .name), declarationNameToString(<Identifier>errorLocation));
1955
2016
}
1956
2017
}
1957
2018
if (result && errorLocation && meaning & SymbolFlags.Value && result.flags & SymbolFlags.Alias) {
0 commit comments