Skip to content

Commit 5330db6

Browse files
committed
Merge pull request #2429 from Microsoft/allowDestructuringInAmbient
Allow destructuring in ambient
2 parents a4b71a1 + c1d9cfe commit 5330db6

25 files changed

+502
-42
lines changed

src/compiler/checker.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12173,9 +12173,6 @@ module ts {
1217312173
function checkGrammarVariableDeclaration(node: VariableDeclaration) {
1217412174
if (node.parent.parent.kind !== SyntaxKind.ForInStatement && node.parent.parent.kind !== SyntaxKind.ForOfStatement) {
1217512175
if (isInAmbientContext(node)) {
12176-
if (isBindingPattern(node.name)) {
12177-
return grammarErrorOnNode(node, Diagnostics.Destructuring_declarations_are_not_allowed_in_ambient_contexts);
12178-
}
1217912176
if (node.initializer) {
1218012177
// Error on equals token which immediate precedes the initializer
1218112178
let equalsTokenLength = "=".length;

src/compiler/declarationEmitter.ts

Lines changed: 108 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -993,7 +993,18 @@ module ts {
993993
}
994994

995995
function emitBindingPattern(bindingPattern: BindingPattern) {
996-
emitCommaList(bindingPattern.elements, emitBindingElement);
996+
// Only select non-omitted expression from the bindingPattern's elements.
997+
// We have to do this to avoid emitting trailing commas.
998+
// For example:
999+
// original: var [, c,,] = [ 2,3,4]
1000+
// emitted: declare var c: number; // instead of declare var c:number, ;
1001+
let elements: Node[] = [];
1002+
for (let element of bindingPattern.elements) {
1003+
if (element.kind !== SyntaxKind.OmittedExpression){
1004+
elements.push(element);
1005+
}
1006+
}
1007+
emitCommaList(elements, emitBindingElement);
9971008
}
9981009

9991010
function emitBindingElement(bindingElement: BindingElement) {
@@ -1291,7 +1302,10 @@ module ts {
12911302
write("...");
12921303
}
12931304
if (isBindingPattern(node.name)) {
1294-
write("_" + indexOf((<FunctionLikeDeclaration>node.parent).parameters, node));
1305+
// For bindingPattern, we can't simply writeTextOfNode from the source file
1306+
// because we want to omit the initializer and using writeTextOfNode will result in initializer get emitted.
1307+
// Therefore, we will have to recursively emit each element in the bindingPattern.
1308+
emitBindingPattern(<BindingPattern>node.name);
12951309
}
12961310
else {
12971311
writeTextOfNode(currentSourceFile, node.name);
@@ -1311,72 +1325,146 @@ module ts {
13111325
}
13121326

13131327
function getParameterDeclarationTypeVisibilityError(symbolAccesibilityResult: SymbolAccessiblityResult): SymbolAccessibilityDiagnostic {
1314-
let diagnosticMessage: DiagnosticMessage;
1328+
let diagnosticMessage: DiagnosticMessage = getParameterDeclarationTypeVisibilityDiagnosticMessage(symbolAccesibilityResult);
1329+
return diagnosticMessage !== undefined ? {
1330+
diagnosticMessage,
1331+
errorNode: node,
1332+
typeName: node.name
1333+
} : undefined;
1334+
}
1335+
1336+
function getParameterDeclarationTypeVisibilityDiagnosticMessage(symbolAccesibilityResult: SymbolAccessiblityResult): DiagnosticMessage {
13151337
switch (node.parent.kind) {
13161338
case SyntaxKind.Constructor:
1317-
diagnosticMessage = symbolAccesibilityResult.errorModuleName ?
1339+
return symbolAccesibilityResult.errorModuleName ?
13181340
symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
13191341
Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
13201342
Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
13211343
Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_private_name_1;
1322-
break;
13231344

13241345
case SyntaxKind.ConstructSignature:
13251346
// Interfaces cannot have parameter types that cannot be named
1326-
diagnosticMessage = symbolAccesibilityResult.errorModuleName ?
1347+
return symbolAccesibilityResult.errorModuleName ?
13271348
Diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 :
13281349
Diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1;
1329-
break;
13301350

13311351
case SyntaxKind.CallSignature:
13321352
// Interfaces cannot have parameter types that cannot be named
1333-
diagnosticMessage = symbolAccesibilityResult.errorModuleName ?
1353+
return symbolAccesibilityResult.errorModuleName ?
13341354
Diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 :
13351355
Diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1;
1336-
break;
13371356

13381357
case SyntaxKind.MethodDeclaration:
13391358
case SyntaxKind.MethodSignature:
13401359
if (node.parent.flags & NodeFlags.Static) {
1341-
diagnosticMessage = symbolAccesibilityResult.errorModuleName ?
1360+
return symbolAccesibilityResult.errorModuleName ?
13421361
symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
13431362
Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
13441363
Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
13451364
Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1;
13461365
}
13471366
else if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) {
1348-
diagnosticMessage = symbolAccesibilityResult.errorModuleName ?
1367+
return symbolAccesibilityResult.errorModuleName ?
13491368
symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
13501369
Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
13511370
Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
13521371
Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_private_name_1;
13531372
}
13541373
else {
13551374
// Interfaces cannot have parameter types that cannot be named
1356-
diagnosticMessage = symbolAccesibilityResult.errorModuleName ?
1375+
return symbolAccesibilityResult.errorModuleName ?
13571376
Diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_name_1_from_private_module_2 :
13581377
Diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_private_name_1;
13591378
}
1360-
break;
13611379

13621380
case SyntaxKind.FunctionDeclaration:
1363-
diagnosticMessage = symbolAccesibilityResult.errorModuleName ?
1381+
return symbolAccesibilityResult.errorModuleName ?
13641382
symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
13651383
Diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
13661384
Diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_private_module_2 :
13671385
Diagnostics.Parameter_0_of_exported_function_has_or_is_using_private_name_1;
1368-
break;
13691386

13701387
default:
13711388
Debug.fail("This is unknown parent for parameter: " + node.parent.kind);
13721389
}
1390+
}
13731391

1374-
return {
1375-
diagnosticMessage,
1376-
errorNode: node,
1377-
typeName: node.name
1378-
};
1392+
function emitBindingPattern(bindingPattern: BindingPattern) {
1393+
// We have to explicitly emit square bracket and bracket because these tokens are not store inside the node.
1394+
if (bindingPattern.kind === SyntaxKind.ObjectBindingPattern) {
1395+
write("{");
1396+
emitCommaList(bindingPattern.elements, emitBindingElement);
1397+
write("}");
1398+
}
1399+
else if (bindingPattern.kind === SyntaxKind.ArrayBindingPattern) {
1400+
write("[");
1401+
let elements = bindingPattern.elements;
1402+
emitCommaList(elements, emitBindingElement);
1403+
if (elements && elements.hasTrailingComma) {
1404+
write(", ");
1405+
}
1406+
write("]");
1407+
}
13791408
}
1409+
1410+
function emitBindingElement(bindingElement: BindingElement) {
1411+
function getBindingElementTypeVisibilityError(symbolAccesibilityResult: SymbolAccessiblityResult): SymbolAccessibilityDiagnostic {
1412+
let diagnosticMessage = getParameterDeclarationTypeVisibilityDiagnosticMessage(symbolAccesibilityResult);
1413+
return diagnosticMessage !== undefined ? {
1414+
diagnosticMessage,
1415+
errorNode: bindingElement,
1416+
typeName: bindingElement.name
1417+
} : undefined;
1418+
}
1419+
1420+
if (bindingElement.kind === SyntaxKind.OmittedExpression) {
1421+
// If bindingElement is an omittedExpression (i.e. containing elision),
1422+
// we will emit blank space (although this may differ from users' original code,
1423+
// it allows emitSeparatedList to write separator appropriately)
1424+
// Example:
1425+
// original: function foo([, x, ,]) {}
1426+
// emit : function foo([ , x, , ]) {}
1427+
write(" ");
1428+
}
1429+
else if (bindingElement.kind === SyntaxKind.BindingElement) {
1430+
if (bindingElement.propertyName) {
1431+
// bindingElement has propertyName property in the following case:
1432+
// { y: [a,b,c] ...} -> bindingPattern will have a property called propertyName for "y"
1433+
// We have to explicitly emit the propertyName before descending into its binding elements.
1434+
// Example:
1435+
// original: function foo({y: [a,b,c]}) {}
1436+
// emit : declare function foo({y: [a, b, c]}: { y: [any, any, any] }) void;
1437+
writeTextOfNode(currentSourceFile, bindingElement.propertyName);
1438+
write(": ");
1439+
1440+
// If bindingElement has propertyName property, then its name must be another bindingPattern of SyntaxKind.ObjectBindingPattern
1441+
emitBindingPattern(<BindingPattern>bindingElement.name);
1442+
}
1443+
else if (bindingElement.name) {
1444+
if (isBindingPattern(bindingElement.name)) {
1445+
// If it is a nested binding pattern, we will recursively descend into each element and emit each one separately.
1446+
// In the case of rest element, we will omit rest element.
1447+
// Example:
1448+
// original: function foo([a, [[b]], c] = [1,[["string"]], 3]) {}
1449+
// emit : declare function foo([a, [[b]], c]: [number, [[string]], number]): void;
1450+
// original with rest: function foo([a, ...c]) {}
1451+
// emit : declare function foo([a, ...c]): void;
1452+
emitBindingPattern(<BindingPattern>bindingElement.name);
1453+
}
1454+
else {
1455+
Debug.assert(bindingElement.name.kind === SyntaxKind.Identifier);
1456+
// If the node is just an identifier, we will simply emit the text associated with the node's name
1457+
// Example:
1458+
// original: function foo({y = 10, x}) {}
1459+
// emit : declare function foo({y, x}: {number, any}): void;
1460+
if (bindingElement.dotDotDotToken) {
1461+
write("...");
1462+
}
1463+
writeTextOfNode(currentSourceFile, bindingElement.name);
1464+
}
1465+
}
1466+
}
1467+
}
13801468
}
13811469

13821470
function emitNode(node: Node) {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//// [declarationEmitDestructuring1.ts]
2+
function foo([a, b, c]: [string, string, string]): void { }
3+
function far([a, [b], [[c]]]: [number, boolean[], string[][]]): void { }
4+
function bar({a1, b1, c1}: { a1: number, b1: boolean, c1: string }): void { }
5+
function baz({a2, b2: {b1, c1}}: { a2: number, b2: { b1: boolean, c1: string } }): void { }
6+
7+
8+
//// [declarationEmitDestructuring1.js]
9+
function foo(_a) {
10+
var a = _a[0], b = _a[1], c = _a[2];
11+
}
12+
function far(_a) {
13+
var a = _a[0], b = _a[1][0], c = _a[2][0][0];
14+
}
15+
function bar(_a) {
16+
var a1 = _a.a1, b1 = _a.b1, c1 = _a.c1;
17+
}
18+
function baz(_a) {
19+
var a2 = _a.a2, _b = _a.b2, b1 = _b.b1, c1 = _b.c1;
20+
}
21+
22+
23+
//// [declarationEmitDestructuring1.d.ts]
24+
declare function foo([a, b, c]: [string, string, string]): void;
25+
declare function far([a, [b], [[c]]]: [number, boolean[], string[][]]): void;
26+
declare function bar({a1, b1, c1}: {
27+
a1: number;
28+
b1: boolean;
29+
c1: string;
30+
}): void;
31+
declare function baz({a2, b2: {b1, c1}}: {
32+
a2: number;
33+
b2: {
34+
b1: boolean;
35+
c1: string;
36+
};
37+
}): void;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
=== tests/cases/compiler/declarationEmitDestructuring1.ts ===
2+
function foo([a, b, c]: [string, string, string]): void { }
3+
>foo : ([a, b, c]: [string, string, string]) => void
4+
>a : string
5+
>b : string
6+
>c : string
7+
8+
function far([a, [b], [[c]]]: [number, boolean[], string[][]]): void { }
9+
>far : ([a, [b], [[c]]]: [number, boolean[], string[][]]) => void
10+
>a : number
11+
>b : boolean
12+
>c : string
13+
14+
function bar({a1, b1, c1}: { a1: number, b1: boolean, c1: string }): void { }
15+
>bar : ({a1, b1, c1}: { a1: number; b1: boolean; c1: string; }) => void
16+
>a1 : number
17+
>b1 : boolean
18+
>c1 : string
19+
>a1 : number
20+
>b1 : boolean
21+
>c1 : string
22+
23+
function baz({a2, b2: {b1, c1}}: { a2: number, b2: { b1: boolean, c1: string } }): void { }
24+
>baz : ({a2, b2: {b1, c1}}: { a2: number; b2: { b1: boolean; c1: string; }; }) => void
25+
>a2 : number
26+
>b2 : unknown
27+
>b1 : boolean
28+
>c1 : string
29+
>a2 : number
30+
>b2 : { b1: boolean; c1: string; }
31+
>b1 : boolean
32+
>c1 : string
33+
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//// [declarationEmitDestructuring2.ts]
2+
function f({x = 10, y: [a, b, c, d] = [1, 2, 3, 4]} = { x: 10, y: [2, 4, 6, 8] }) { }
3+
function g([a, b, c, d] = [1, 2, 3, 4]) { }
4+
function h([a, [b], [[c]], {x = 10, y: [a, b, c], z: {a1, b1}}]){ }
5+
function h1([a, [b], [[c]], {x = 10, y = [1, 2, 3], z: {a1, b1}}]){ }
6+
7+
//// [declarationEmitDestructuring2.js]
8+
function f(_a) {
9+
var _b = _a === void 0 ? { x: 10, y: [2, 4, 6, 8] } : _a, _c = _b.x, x = _c === void 0 ? 10 : _c, _d = _b.y, _e = _d === void 0 ? [1, 2, 3, 4] : _d, a = _e[0], b = _e[1], c = _e[2], d = _e[3];
10+
}
11+
function g(_a) {
12+
var _b = _a === void 0 ? [1, 2, 3, 4] : _a, a = _b[0], b = _b[1], c = _b[2], d = _b[3];
13+
}
14+
function h(_a) {
15+
var a = _a[0], b = _a[1][0], c = _a[2][0][0], _b = _a[3], _c = _b.x, x = _c === void 0 ? 10 : _c, _d = _b.y, a = _d[0], b = _d[1], c = _d[2], _e = _b.z, a1 = _e.a1, b1 = _e.b1;
16+
}
17+
function h1(_a) {
18+
var a = _a[0], b = _a[1][0], c = _a[2][0][0], _b = _a[3], _c = _b.x, x = _c === void 0 ? 10 : _c, _d = _b.y, y = _d === void 0 ? [1, 2, 3] : _d, _e = _b.z, a1 = _e.a1, b1 = _e.b1;
19+
}
20+
21+
22+
//// [declarationEmitDestructuring2.d.ts]
23+
declare function f({x, y: [a, b, c, d]}?: {
24+
x: number;
25+
y: [number, number, number, number];
26+
}): void;
27+
declare function g([a, b, c, d]?: [number, number, number, number]): void;
28+
declare function h([a, [b], [[c]], {x, y: [a, b, c], z: {a1, b1}}]: [any, [any], [[any]], {
29+
x?: number;
30+
y: [any, any, any];
31+
z: {
32+
a1: any;
33+
b1: any;
34+
};
35+
}]): void;
36+
declare function h1([a, [b], [[c]], {x, y, z: {a1, b1}}]: [any, [any], [[any]], {
37+
x?: number;
38+
y?: number[];
39+
z: {
40+
a1: any;
41+
b1: any;
42+
};
43+
}]): void;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
=== tests/cases/compiler/declarationEmitDestructuring2.ts ===
2+
function f({x = 10, y: [a, b, c, d] = [1, 2, 3, 4]} = { x: 10, y: [2, 4, 6, 8] }) { }
3+
>f : ({x = 10, y: [a, b, c, d] = [1, 2, 3, 4]}?: { x: number; y: [number, number, number, number]; }) => void
4+
>x : number
5+
>y : unknown
6+
>a : number
7+
>b : number
8+
>c : number
9+
>d : number
10+
>[1, 2, 3, 4] : [number, number, number, number]
11+
>{ x: 10, y: [2, 4, 6, 8] } : { x: number; y: [number, number, number, number]; }
12+
>x : number
13+
>y : [number, number, number, number]
14+
>[2, 4, 6, 8] : [number, number, number, number]
15+
16+
function g([a, b, c, d] = [1, 2, 3, 4]) { }
17+
>g : ([a, b, c, d]?: [number, number, number, number]) => void
18+
>a : number
19+
>b : number
20+
>c : number
21+
>d : number
22+
>[1, 2, 3, 4] : [number, number, number, number]
23+
24+
function h([a, [b], [[c]], {x = 10, y: [a, b, c], z: {a1, b1}}]){ }
25+
>h : ([a, [b], [[c]], {x = 10, y: [a, b, c], z: {a1, b1}}]: [any, [any], [[any]], { x?: number; y: [any, any, any]; z: { a1: any; b1: any; }; }]) => void
26+
>a : any
27+
>b : any
28+
>c : any
29+
>x : number
30+
>y : unknown
31+
>a : any
32+
>b : any
33+
>c : any
34+
>z : unknown
35+
>a1 : any
36+
>b1 : any
37+
38+
function h1([a, [b], [[c]], {x = 10, y = [1, 2, 3], z: {a1, b1}}]){ }
39+
>h1 : ([a, [b], [[c]], {x = 10, y = [1, 2, 3], z: {a1, b1}}]: [any, [any], [[any]], { x?: number; y?: number[]; z: { a1: any; b1: any; }; }]) => void
40+
>a : any
41+
>b : any
42+
>c : any
43+
>x : number
44+
>y : number[]
45+
>[1, 2, 3] : number[]
46+
>z : unknown
47+
>a1 : any
48+
>b1 : any
49+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//// [declarationEmitDestructuring3.ts]
2+
function bar([x, z, ...w]) { }
3+
function foo([x, ...y] = [1, "string", true]) { }
4+
5+
6+
7+
//// [declarationEmitDestructuring3.js]
8+
function bar(_a) {
9+
var x = _a[0], z = _a[1], w = _a.slice(2);
10+
}
11+
function foo(_a) {
12+
var _b = _a === void 0 ? [1, "string", true] : _a, x = _b[0], y = _b.slice(1);
13+
}
14+
15+
16+
//// [declarationEmitDestructuring3.d.ts]
17+
declare function bar([x, z, ...w]: any[]): void;
18+
declare function foo([x, ...y]?: (string | number | boolean)[]): void;

0 commit comments

Comments
 (0)