diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 50c3adf13107a..4909f7136d949 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1150,8 +1150,8 @@ namespace ts { currentFlow = finishFlowLabel(postExpressionLabel); } - function bindInitializedVariableFlow(node: VariableDeclaration | BindingElement) { - const name = node.name; + function bindInitializedVariableFlow(node: VariableDeclaration | ArrayBindingElement) { + const name = !isOmittedExpression(node) ? node.name : undefined; if (isBindingPattern(name)) { for (const child of name.elements) { bindInitializedVariableFlow(child); diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 565d01bf024dd..45c784a5ad7f1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2505,8 +2505,8 @@ namespace ts { } } - function buildBindingElementDisplay(bindingElement: BindingElement, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { - if (bindingElement.kind === SyntaxKind.OmittedExpression) { + function buildBindingElementDisplay(bindingElement: ArrayBindingElement, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { + if (isOmittedExpression(bindingElement)) { return; } Debug.assert(bindingElement.kind === SyntaxKind.BindingElement); @@ -3125,7 +3125,7 @@ namespace ts { } // Return the type implied by an object binding pattern - function getTypeFromObjectBindingPattern(pattern: BindingPattern, includePatternInType: boolean, reportErrors: boolean): Type { + function getTypeFromObjectBindingPattern(pattern: ObjectBindingPattern, includePatternInType: boolean, reportErrors: boolean): Type { const members = createMap(); let hasComputedProperties = false; forEach(pattern.elements, e => { @@ -3156,11 +3156,12 @@ namespace ts { // Return the type implied by an array binding pattern function getTypeFromArrayBindingPattern(pattern: BindingPattern, includePatternInType: boolean, reportErrors: boolean): Type { const elements = pattern.elements; - if (elements.length === 0 || elements[elements.length - 1].dotDotDotToken) { + const lastElement = lastOrUndefined(elements); + if (elements.length === 0 || (!isOmittedExpression(lastElement) && lastElement.dotDotDotToken)) { return languageVersion >= ScriptTarget.ES6 ? createIterableType(anyType) : anyArrayType; } // If the pattern has at least one element, and no rest element, then it should imply a tuple type. - const elementTypes = map(elements, e => e.kind === SyntaxKind.OmittedExpression ? anyType : getTypeFromBindingElement(e, includePatternInType, reportErrors)); + const elementTypes = map(elements, e => isOmittedExpression(e) ? anyType : getTypeFromBindingElement(e, includePatternInType, reportErrors)); let result = createTupleType(elementTypes); if (includePatternInType) { result = cloneTypeReference(result); @@ -3178,8 +3179,8 @@ namespace ts { // the parameter. function getTypeFromBindingPattern(pattern: BindingPattern, includePatternInType?: boolean, reportErrors?: boolean): Type { return pattern.kind === SyntaxKind.ObjectBindingPattern - ? getTypeFromObjectBindingPattern(pattern, includePatternInType, reportErrors) - : getTypeFromArrayBindingPattern(pattern, includePatternInType, reportErrors); + ? getTypeFromObjectBindingPattern(pattern, includePatternInType, reportErrors) + : getTypeFromArrayBindingPattern(pattern, includePatternInType, reportErrors); } // Return the type associated with a variable, parameter, or property declaration. In the simple case this is the type @@ -12413,7 +12414,7 @@ namespace ts { function assignBindingElementTypes(node: VariableLikeDeclaration) { if (isBindingPattern(node.name)) { for (const element of (node.name).elements) { - if (element.kind !== SyntaxKind.OmittedExpression) { + if (!isOmittedExpression(element)) { if (element.name.kind === SyntaxKind.Identifier) { getSymbolLinks(getSymbolOfNode(element)).type = getTypeForBindingElement(element); } @@ -13889,7 +13890,12 @@ namespace ts { pattern: BindingPattern, predicateVariableNode: Node, predicateVariableName: string) { - for (const { name } of pattern.elements) { + for (const element of pattern.elements) { + if (isOmittedExpression(element)) { + continue; + } + + const name = element.name; if (name.kind === SyntaxKind.Identifier && (name).text === predicateVariableName) { error(predicateVariableNode, @@ -19999,7 +20005,7 @@ namespace ts { else { const elements = (name).elements; for (const element of elements) { - if (element.kind !== SyntaxKind.OmittedExpression) { + if (!isOmittedExpression(element)) { checkGrammarNameInLetOrConstDeclarations(element.name); } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 0f96b04df9ab4..51808592bd0d3 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -554,9 +554,9 @@ const _super = (function (geti, seti) { // Binding patterns case SyntaxKind.ObjectBindingPattern: - return emitObjectBindingPattern(node); + return emitObjectBindingPattern(node); case SyntaxKind.ArrayBindingPattern: - return emitArrayBindingPattern(node); + return emitArrayBindingPattern(node); case SyntaxKind.BindingElement: return emitBindingElement(node); diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 1930481115c34..16eeb86f59a73 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -368,13 +368,13 @@ namespace ts { return node; } - export function createArrayBindingPattern(elements: BindingElement[], location?: TextRange) { + export function createArrayBindingPattern(elements: ArrayBindingElement[], location?: TextRange) { const node = createNode(SyntaxKind.ArrayBindingPattern, location); node.elements = createNodeArray(elements); return node; } - export function updateArrayBindingPattern(node: ArrayBindingPattern, elements: BindingElement[]) { + export function updateArrayBindingPattern(node: ArrayBindingPattern, elements: ArrayBindingElement[]) { if (node.elements !== elements) { return updateNode(createArrayBindingPattern(elements, node), node); } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index e14a60eb10ca3..6e6b372b1c1a6 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4815,9 +4815,9 @@ namespace ts { // DECLARATIONS - function parseArrayBindingElement(): BindingElement { + function parseArrayBindingElement(): ArrayBindingElement { if (token() === SyntaxKind.CommaToken) { - return createNode(SyntaxKind.OmittedExpression); + return createNode(SyntaxKind.OmittedExpression); } const node = createNode(SyntaxKind.BindingElement); node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken); @@ -4842,16 +4842,16 @@ namespace ts { return finishNode(node); } - function parseObjectBindingPattern(): BindingPattern { - const node = createNode(SyntaxKind.ObjectBindingPattern); + function parseObjectBindingPattern(): ObjectBindingPattern { + const node = createNode(SyntaxKind.ObjectBindingPattern); parseExpected(SyntaxKind.OpenBraceToken); node.elements = parseDelimitedList(ParsingContext.ObjectBindingElements, parseObjectBindingElement); parseExpected(SyntaxKind.CloseBraceToken); return finishNode(node); } - function parseArrayBindingPattern(): BindingPattern { - const node = createNode(SyntaxKind.ArrayBindingPattern); + function parseArrayBindingPattern(): ArrayBindingPattern { + const node = createNode(SyntaxKind.ArrayBindingPattern); parseExpected(SyntaxKind.OpenBracketToken); node.elements = parseDelimitedList(ParsingContext.ArrayBindingElements, parseArrayBindingElement); parseExpected(SyntaxKind.CloseBracketToken); diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index e211ca61f36d0..c6079ee13786a 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -326,12 +326,15 @@ namespace ts { } for (let i = 0; i < numElements; i++) { const element = elements[i]; - if (name.kind === SyntaxKind.ObjectBindingPattern) { + if (isOmittedExpression(element)) { + continue; + } + else if (name.kind === SyntaxKind.ObjectBindingPattern) { // Rewrite element to a declaration with an initializer that fetches property const propName = element.propertyName || element.name; emitBindingElement(element, createDestructuringPropertyAccess(value, propName)); } - else if (element.kind !== SyntaxKind.OmittedExpression) { + else { if (!element.dotDotDotToken) { // Rewrite element to a declaration that accesses array element at index i emitBindingElement(element, createElementAccess(value, i)); diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index 4e994c1acb0fc..5f336da54602f 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -1945,7 +1945,9 @@ namespace ts { } else { for (const element of (node).elements) { - visit(element.name); + if (!isOmittedExpression(element)) { + visit(element.name); + } } } } @@ -2289,7 +2291,9 @@ namespace ts { const name = decl.name; if (isBindingPattern(name)) { for (const element of name.elements) { - processLoopVariableDeclaration(element, loopParameters, loopOutParameters); + if (!isOmittedExpression(element)) { + processLoopVariableDeclaration(element, loopParameters, loopOutParameters); + } } } else { diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 144420b7ae958..61db452cf50ba 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -660,7 +660,9 @@ namespace ts { function addExportMemberAssignmentsForBindingName(resultStatements: Statement[], name: BindingName): void { if (isBindingPattern(name)) { for (const element of name.elements) { - addExportMemberAssignmentsForBindingName(resultStatements, element.name); + if (!isOmittedExpression(element)) { + addExportMemberAssignmentsForBindingName(resultStatements, element.name); + } } } else { diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index d303dd580ef75..7b013e18e7dc0 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -1368,7 +1368,11 @@ namespace ts { exportedFunctionDeclarations.push(createDeclarationExport(node)); } - function hoistBindingElement(node: VariableDeclaration | BindingElement, isExported: boolean): void { + function hoistBindingElement(node: VariableDeclaration | ArrayBindingElement, isExported: boolean): void { + if (isOmittedExpression(node)) { + return; + } + const name = node.name; if (isIdentifier(name)) { hoistVariableDeclaration(getSynthesizedClone(name)); @@ -1381,11 +1385,11 @@ namespace ts { } } - function hoistExportedBindingElement(node: VariableDeclaration | BindingElement) { + function hoistExportedBindingElement(node: VariableDeclaration | ArrayBindingElement) { hoistBindingElement(node, /*isExported*/ true); } - function hoistNonExportedBindingElement(node: VariableDeclaration | BindingElement) { + function hoistNonExportedBindingElement(node: VariableDeclaration | ArrayBindingElement) { hoistBindingElement(node, /*isExported*/ false); } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index efb247ba66379..e0765ed702554 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -2187,7 +2187,7 @@ namespace ts { * * @param node The function expression node. */ - function visitFunctionExpression(node: FunctionExpression) { + function visitFunctionExpression(node: FunctionExpression): Expression { if (nodeIsMissing(node.body)) { return createOmittedExpression(); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d880b6b567b5b..4a9338587d273 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -688,14 +688,20 @@ namespace ts { } export interface BindingPattern extends Node { - elements: NodeArray; + elements: NodeArray; } // @kind(SyntaxKind.ObjectBindingPattern) - export interface ObjectBindingPattern extends BindingPattern { } + export interface ObjectBindingPattern extends BindingPattern { + elements: NodeArray; + } + + export type ArrayBindingElement = BindingElement | OmittedExpression; // @kind(SyntaxKind.ArrayBindingPattern) - export interface ArrayBindingPattern extends BindingPattern { } + export interface ArrayBindingPattern extends BindingPattern { + elements: NodeArray; + } /** * Several node kinds share function-like features such as a signature, @@ -868,7 +874,9 @@ namespace ts { } // @kind(SyntaxKind.OmittedExpression) - export interface OmittedExpression extends Expression { } + export interface OmittedExpression extends Expression { + _omittedExpressionBrand: any; + } // Represents an expression that is elided as part of a transformation to emit comments on a // not-emitted node. The 'expression' property of a NotEmittedExpression should be emitted. diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 9ea74abbf7608..bec83d262a350 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3701,6 +3701,12 @@ namespace ts { return node.kind === SyntaxKind.BindingElement; } + export function isArrayBindingElement(node: Node): node is ArrayBindingElement { + const kind = node.kind; + return kind === SyntaxKind.BindingElement + || kind === SyntaxKind.OmittedExpression; + } + // Expression export function isPropertyAccessExpression(node: Node): node is PropertyAccessExpression { @@ -3817,6 +3823,10 @@ namespace ts { || isPartiallyEmittedExpression(node); } + export function isOmittedExpression(node: Node): node is OmittedExpression { + return node.kind === SyntaxKind.OmittedExpression; + } + // Misc export function isTemplateSpan(node: Node): node is TemplateSpan { diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 648fd235c44d1..1e0cadaaa6b99 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -757,7 +757,7 @@ namespace ts { case SyntaxKind.ArrayBindingPattern: return updateArrayBindingPattern(node, - visitNodes((node).elements, visitor, isBindingElement)); + visitNodes((node).elements, visitor, isArrayBindingElement)); case SyntaxKind.BindingElement: return updateBindingElement(node, diff --git a/src/services/completions.ts b/src/services/completions.ts index d933c89434412..5dc15b1cb099d 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1192,7 +1192,7 @@ namespace ts.Completions { } if (canGetType) { typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer); - existingMembers = (objectLikeContainer).elements; + existingMembers = (objectLikeContainer).elements; } } else { diff --git a/tests/baselines/reference/exportArrayBindingPattern.js b/tests/baselines/reference/exportArrayBindingPattern.js new file mode 100644 index 0000000000000..386419e5725eb --- /dev/null +++ b/tests/baselines/reference/exportArrayBindingPattern.js @@ -0,0 +1,11 @@ +//// [exportArrayBindingPattern.ts] +// issue: https://github.com/Microsoft/TypeScript/issues/10778 +const [a, , b] = [1, 2, 3]; +export { a, b }; + +//// [exportArrayBindingPattern.js] +"use strict"; +// issue: https://github.com/Microsoft/TypeScript/issues/10778 +var _a = [1, 2, 3], a = _a[0], b = _a[2]; +exports.a = a; +exports.b = b; diff --git a/tests/baselines/reference/exportArrayBindingPattern.symbols b/tests/baselines/reference/exportArrayBindingPattern.symbols new file mode 100644 index 0000000000000..96fb5cabc881d --- /dev/null +++ b/tests/baselines/reference/exportArrayBindingPattern.symbols @@ -0,0 +1,10 @@ +=== tests/cases/compiler/exportArrayBindingPattern.ts === +// issue: https://github.com/Microsoft/TypeScript/issues/10778 +const [a, , b] = [1, 2, 3]; +>a : Symbol(a, Decl(exportArrayBindingPattern.ts, 1, 7)) +>b : Symbol(b, Decl(exportArrayBindingPattern.ts, 1, 11)) + +export { a, b }; +>a : Symbol(a, Decl(exportArrayBindingPattern.ts, 2, 8)) +>b : Symbol(b, Decl(exportArrayBindingPattern.ts, 2, 11)) + diff --git a/tests/baselines/reference/exportArrayBindingPattern.types b/tests/baselines/reference/exportArrayBindingPattern.types new file mode 100644 index 0000000000000..bfc1e461dd5fa --- /dev/null +++ b/tests/baselines/reference/exportArrayBindingPattern.types @@ -0,0 +1,15 @@ +=== tests/cases/compiler/exportArrayBindingPattern.ts === +// issue: https://github.com/Microsoft/TypeScript/issues/10778 +const [a, , b] = [1, 2, 3]; +>a : number +> : undefined +>b : number +>[1, 2, 3] : [number, number, number] +>1 : number +>2 : number +>3 : number + +export { a, b }; +>a : number +>b : number + diff --git a/tests/cases/compiler/exportArrayBindingPattern.ts b/tests/cases/compiler/exportArrayBindingPattern.ts new file mode 100644 index 0000000000000..e161962efe890 --- /dev/null +++ b/tests/cases/compiler/exportArrayBindingPattern.ts @@ -0,0 +1,4 @@ +// @module: commonjs +// issue: https://github.com/Microsoft/TypeScript/issues/10778 +const [a, , b] = [1, 2, 3]; +export { a, b }; \ No newline at end of file