Clean up destructuring

This commit is contained in:
Ron Buckton 2016-11-14 14:20:25 -08:00
parent d5b9263433
commit 08f467bd4c
10 changed files with 914 additions and 873 deletions

View File

@ -1280,7 +1280,7 @@ namespace ts {
function bindInitializedVariableFlow(node: VariableDeclaration | ArrayBindingElement) {
const name = !isOmittedExpression(node) ? node.name : undefined;
if (isBindingPattern(name)) {
for (const child of name.elements) {
for (const child of <ArrayBindingElement[]>name.elements) {
bindInitializedVariableFlow(child);
}
}
@ -2629,7 +2629,7 @@ namespace ts {
}
// parameters with object rest destructuring are ES Next syntax
if (subtreeFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) {
if (subtreeFlags & TransformFlags.ContainsObjectRest) {
transformFlags |= TransformFlags.AssertESNext;
}
@ -2867,7 +2867,7 @@ namespace ts {
}
// function declarations with object rest destructuring are ES Next syntax
if (subtreeFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) {
if (subtreeFlags & TransformFlags.ContainsObjectRest) {
transformFlags |= TransformFlags.AssertESNext;
}
@ -2909,7 +2909,7 @@ namespace ts {
}
// function expressions with object rest destructuring are ES Next syntax
if (subtreeFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) {
if (subtreeFlags & TransformFlags.ContainsObjectRest) {
transformFlags |= TransformFlags.AssertESNext;
}
@ -2952,7 +2952,7 @@ namespace ts {
}
// arrow functions with object rest destructuring are ES Next syntax
if (subtreeFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) {
if (subtreeFlags & TransformFlags.ContainsObjectRest) {
transformFlags |= TransformFlags.AssertESNext;
}
@ -2982,16 +2982,11 @@ namespace ts {
function computeVariableDeclaration(node: VariableDeclaration, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
const nameKind = node.name.kind;
transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern;
// A VariableDeclaration with an object binding pattern is ES2015 syntax
// and possibly ESNext syntax if it contains an object binding pattern
if (nameKind === SyntaxKind.ObjectBindingPattern) {
transformFlags |= TransformFlags.AssertESNext | TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern;
}
// A VariableDeclaration with an object binding pattern is ES2015 syntax.
else if (nameKind === SyntaxKind.ArrayBindingPattern) {
transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern;
// A VariableDeclaration containing ObjectRest is ESNext syntax
if (subtreeFlags & TransformFlags.ContainsObjectRest) {
transformFlags |= TransformFlags.AssertESNext;
}
// Type annotations are TypeScript syntax.
@ -3213,13 +3208,6 @@ namespace ts {
transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsSpread | TransformFlags.ContainsObjectSpread;
break;
case SyntaxKind.BindingElement:
transformFlags |= TransformFlags.AssertES2015;
if ((<BindingElement>node).dotDotDotToken) {
transformFlags |= TransformFlags.ContainsRest;
}
break;
case SyntaxKind.SuperKeyword:
// This node is ES6 syntax.
transformFlags |= TransformFlags.AssertES2015;
@ -3232,7 +3220,7 @@ namespace ts {
case SyntaxKind.ObjectBindingPattern:
transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern;
if (subtreeFlags & TransformFlags.ContainsSpread) {
if (subtreeFlags & TransformFlags.ContainsRest) {
transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsObjectRest;
}
excludeFlags = TransformFlags.BindingPatternExcludes;
@ -3243,6 +3231,13 @@ namespace ts {
excludeFlags = TransformFlags.BindingPatternExcludes;
break;
case SyntaxKind.BindingElement:
transformFlags |= TransformFlags.AssertES2015;
if ((<BindingElement>node).dotDotDotToken) {
transformFlags |= TransformFlags.ContainsRest;
}
break;
case SyntaxKind.Decorator:
// This node is TypeScript syntax, and marks its container as also being TypeScript syntax.
transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsDecorators;

File diff suppressed because it is too large Load Diff

View File

@ -3,80 +3,21 @@
/*@internal*/
namespace ts {
type EffectiveBindingOrAssignmentElement = VariableDeclaration | ParameterDeclaration | BindingElement | ObjectLiteralElementLike | Expression;
type EffectiveObjectBindingOrAssignmentPattern = ObjectBindingPattern | ObjectLiteralExpression;
type EffectiveArrayBindingOrAssignmentPattern = ArrayBindingPattern | ArrayLiteralExpression;
type EffectiveBindingOrAssignmentPattern = EffectiveObjectBindingOrAssignmentPattern | EffectiveArrayBindingOrAssignmentPattern;
type EffectiveBindingOrAssignmentTarget = EffectiveBindingOrAssignmentPattern | Expression;
type EffectiveBindingOrAssignmentRestIndicator = DotDotDotToken | SpreadElement | SpreadAssignment;
interface FlattenHost {
context: TransformationContext;
level: FlattenLevel;
recordTempVariablesInLine: boolean;
emitExpression: (value: Expression) => void;
emitBindingOrAssignment: (target: EffectiveBindingOrAssignmentTarget, value: Expression, location: TextRange, original: Node) => void;
createArrayBindingOrAssignmentPattern: (elements: EffectiveBindingOrAssignmentElement[]) => EffectiveArrayBindingOrAssignmentPattern;
createObjectBindingOrAssignmentPattern: (elements: EffectiveBindingOrAssignmentElement[]) => EffectiveObjectBindingOrAssignmentPattern;
createArrayBindingOrAssignmentElement: (node: Identifier) => EffectiveBindingOrAssignmentElement;
emitBindingOrAssignment: (target: BindingOrAssignmentElementTarget, value: Expression, location: TextRange, original: Node) => void;
createArrayBindingOrAssignmentPattern: (elements: BindingOrAssignmentElement[]) => ArrayBindingOrAssignmentPattern;
createObjectBindingOrAssignmentPattern: (elements: BindingOrAssignmentElement[]) => ObjectBindingOrAssignmentPattern;
createArrayBindingOrAssignmentElement: (node: Identifier) => BindingOrAssignmentElement;
visitor?: (node: Node) => VisitResult<Node>;
}
export const enum FlattenLevel {
ObjectRest,
All,
}
export const enum FlattenOutput {
Expression,
Declarations
}
export const enum FlattenOptions {
NeedsValue = 1 << 0,
SkipInitializer = 1 << 1,
RecordTempVariablesInLine = 1 << 2,
}
export function flattenDestructuring(
output: FlattenOutput.Expression,
level: FlattenLevel,
node: VariableDeclaration | DestructuringAssignment,
visitor: (node: Node) => VisitResult<Node>,
context: TransformationContext,
options: FlattenOptions,
createAssignment?: (name: Identifier, value: Expression, location?: TextRange) => Expression): Expression;
export function flattenDestructuring(
output: FlattenOutput.Declarations,
level: FlattenLevel,
node: VariableDeclaration | ParameterDeclaration,
visitor: (node: Node) => VisitResult<Node>,
context: TransformationContext,
options: FlattenOptions,
boundValue?: Expression): VariableDeclaration[];
export function flattenDestructuring(
_output: FlattenOutput,
_level: FlattenLevel,
_node: VariableDeclaration | ParameterDeclaration | DestructuringAssignment,
_visitor: (node: Node) => VisitResult<Node>,
_context: TransformationContext,
_options: FlattenOptions,
_valueOrCallback?: Expression | ((name: Identifier, value: Expression, location?: TextRange) => Expression)): Expression | VariableDeclaration[] {
// const flattenMode = flags & FlattenFlags.FlattenMask;
// const outputMode = flags & FlattenFlags.OutputMask;
// const options = flags & FlattenFlags.OptionsMask;
// if (outputMode === FlattenFlags.OutputExpressions) {
// return flattenDestructuringToExpression(context, <VariableDeclaration | DestructuringAssignment>node, !(options & FlattenFlags.NeedsValue), flattenMode, createAssignmentCallback, visitor);
// }
// else {
// return flattenDestructuringToDeclarations(
// context,
// <VariableDeclaration | ParameterDeclaration>node,
// boundValue)
// }
return;
ObjectRest,
}
/**
@ -90,7 +31,7 @@ namespace ts {
* @param createAssignmentCallback A callback used to create the assignment expression.
* @param visitor A visitor used to visit initializers.
*/
export function flattenDestructuringToExpression(
export function flattenDestructuringAssignment(
context: TransformationContext,
node: VariableDeclaration | DestructuringAssignment,
needsValue: boolean,
@ -120,9 +61,9 @@ namespace ts {
recordTempVariablesInLine: false,
emitExpression,
emitBindingOrAssignment,
createArrayBindingOrAssignmentPattern: createEffectiveArrayAssignmentPattern,
createObjectBindingOrAssignmentPattern: createEffectiveObjectAssignmentPattern,
createArrayBindingOrAssignmentElement: createEffectiveAssignmentElement,
createArrayBindingOrAssignmentPattern: makeArrayAssignmentPattern,
createObjectBindingOrAssignmentPattern: makeObjectAssignmentPattern,
createArrayBindingOrAssignmentElement: makeAssignmentElement,
visitor
};
@ -148,7 +89,7 @@ namespace ts {
}
}
flattenEffectiveBindingElement(host, node, value, location, /*skipInitializer*/ isDestructuringAssignment(node));
flattenBindingOrAssignmentElement(host, node, value, location, /*skipInitializer*/ isDestructuringAssignment(node));
if (value && needsValue) {
expressions.push(value);
@ -164,7 +105,7 @@ namespace ts {
expressions.push(expression);
}
function emitBindingOrAssignment(target: EffectiveBindingOrAssignmentTarget, value: Expression, location: TextRange, original: Node) {
function emitBindingOrAssignment(target: BindingOrAssignmentElementTarget, value: Expression, location: TextRange, original: Node) {
Debug.assertNode(target, createAssignmentCallback ? isIdentifier : isExpression);
const expression = createAssignmentCallback
? createAssignmentCallback(<Identifier>target, value, location)
@ -183,7 +124,7 @@ namespace ts {
* @param recordTempVariablesInLine Indicates whether temporary variables should be recored in-line.
* @param level Indicates the extent to which flattening should occur.
*/
export function flattenDestructuringToDeclarations(
export function flattenDestructuringBinding(
context: TransformationContext,
node: VariableDeclaration | ParameterDeclaration,
boundValue: Expression | undefined,
@ -201,13 +142,13 @@ namespace ts {
recordTempVariablesInLine,
emitExpression,
emitBindingOrAssignment,
createArrayBindingOrAssignmentPattern: createEffectiveArrayBindingPattern,
createObjectBindingOrAssignmentPattern: createEffectiveObjectBindingPattern,
createArrayBindingOrAssignmentElement: createEffectiveBindingElement,
createArrayBindingOrAssignmentPattern: makeArrayBindingPattern,
createObjectBindingOrAssignmentPattern: makeObjectBindingPattern,
createArrayBindingOrAssignmentElement: makeBindingElement,
visitor
};
flattenEffectiveBindingElement(host, node, boundValue, node, skipInitializer);
flattenBindingOrAssignmentElement(host, node, boundValue, node, skipInitializer);
if (pendingExpressions) {
const temp = createTempVariable(/*recordTempVariable*/ undefined);
@ -248,7 +189,7 @@ namespace ts {
pendingExpressions = append(pendingExpressions, value);
}
function emitBindingOrAssignment(target: EffectiveBindingOrAssignmentTarget, value: Expression, location: TextRange, original: Node) {
function emitBindingOrAssignment(target: BindingOrAssignmentElementTarget, value: Expression, location: TextRange, original: Node) {
Debug.assertNode(target, isBindingName);
if (pendingExpressions) {
value = inlineExpressions(append(pendingExpressions, value));
@ -258,14 +199,14 @@ namespace ts {
}
}
function flattenEffectiveBindingElement(
function flattenBindingOrAssignmentElement(
host: FlattenHost,
bindingElement: EffectiveBindingOrAssignmentElement,
bindingElement: BindingOrAssignmentElement,
boundValue: Expression | undefined,
location: TextRange,
skipInitializer?: boolean) {
if (!skipInitializer) {
const initializer = visitNode(getInitializerOfEffectiveBindingElement(bindingElement), host.visitor, isExpression);
const initializer = visitNode(getInitializerOfBindingOrAssignmentElement(bindingElement), host.visitor, isExpression);
if (initializer) {
// Combine value and initializer
boundValue = boundValue ? createDefaultValueCheck(host, boundValue, initializer, location) : initializer;
@ -275,39 +216,36 @@ namespace ts {
boundValue = createVoidZero();
}
}
const bindingTarget = getTargetOfEffectiveBindingElement(bindingElement);
if (!isEffectiveBindingPattern(bindingTarget)) {
host.emitBindingOrAssignment(bindingTarget, boundValue, location, /*original*/ bindingElement);
const bindingTarget = getTargetOfBindingOrAssignmentElement(bindingElement);
if (isObjectBindingOrAssignmentPattern(bindingTarget)) {
flattenObjectBindingOrAssignmentPattern(host, bindingElement, bindingTarget, boundValue, location);
}
else if (isArrayBindingOrAssignmentPattern(bindingTarget)) {
flattenArrayBindingOrAssignmentPattern(host, bindingElement, bindingTarget, boundValue, location);
}
else {
const elements = getElementsOfEffectiveBindingPattern(bindingTarget);
const numElements = elements.length;
if (numElements !== 1) {
// For anything other than a single-element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once. Additionally, if we have zero elements
// we need to emit *something* to ensure that in case a 'var' keyword was already emitted,
// so in that case, we'll intentionally create that temporary.
const reuseIdentifierExpressions = !isDeclarationBindingElement(bindingElement) || numElements !== 0;
boundValue = ensureIdentifier(host, boundValue, reuseIdentifierExpressions, location);
}
if (isEffectiveObjectBindingPattern(bindingTarget)) {
flattenEffectiveObjectBindingElements(host, bindingTarget, elements, boundValue, location);
}
else {
flattenEffectiveArrayBindingElements(host, bindingTarget, elements, boundValue, location);
}
host.emitBindingOrAssignment(bindingTarget, boundValue, location, /*original*/ bindingElement);
}
}
function flattenEffectiveObjectBindingElements(host: FlattenHost, bindingTarget: EffectiveObjectBindingOrAssignmentPattern, elements: EffectiveBindingOrAssignmentElement[], boundValue: Expression, location: TextRange) {
let bindingElements: EffectiveBindingOrAssignmentElement[];
function flattenObjectBindingOrAssignmentPattern(host: FlattenHost, parentElement: BindingOrAssignmentElement, bindingTarget: ObjectBindingOrAssignmentPattern, boundValue: Expression, location: TextRange) {
const elements = getElementsOfBindingOrAssignmentPattern(bindingTarget);
const numElements = elements.length;
if (numElements !== 1) {
// For anything other than a single-element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once. Additionally, if we have zero elements
// we need to emit *something* to ensure that in case a 'var' keyword was already emitted,
// so in that case, we'll intentionally create that temporary.
const reuseIdentifierExpressions = !isDeclarationBindingElement(parentElement) || numElements !== 0;
boundValue = ensureIdentifier(host, boundValue, reuseIdentifierExpressions, location);
}
let bindingElements: BindingOrAssignmentElement[];
for (let i = 0; i < numElements; i++) {
const element = elements[i];
if (!getEffectiveRestIndicator(element)) {
if (host.level <= FlattenLevel.ObjectRest
if (!getRestIndicatorOfBindingOrAssignmentElement(element)) {
if (host.level >= FlattenLevel.ObjectRest
&& !(element.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest))
&& !(getTargetOfEffectiveBindingElement(element).transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest))) {
&& !(getTargetOfBindingOrAssignmentElement(element).transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest))) {
bindingElements = append(bindingElements, element);
}
else {
@ -315,9 +253,9 @@ namespace ts {
host.emitBindingOrAssignment(host.createObjectBindingOrAssignmentPattern(bindingElements), boundValue, location, bindingTarget);
bindingElements = undefined;
}
const propertyName = getEffectivePropertyNameOfEffectiveBindingElement(element);
const propertyName = getPropertyNameOfBindingOrAssignmentElement(element);
const value = createDestructuringPropertyAccess(host, boundValue, propertyName);
flattenEffectiveBindingElement(host, element, value, /*location*/ element);
flattenBindingOrAssignmentElement(host, element, value, /*location*/ element);
}
}
else if (i === numElements - 1) {
@ -326,7 +264,7 @@ namespace ts {
bindingElements = undefined;
}
const value = createRestCall(boundValue, elements, bindingTarget);
flattenEffectiveBindingElement(host, element, value, element);
flattenBindingOrAssignmentElement(host, element, value, element);
}
}
if (bindingElements) {
@ -334,16 +272,27 @@ namespace ts {
}
}
function flattenEffectiveArrayBindingElements(host: FlattenHost, bindingTarget: EffectiveArrayBindingOrAssignmentPattern, elements: EffectiveBindingOrAssignmentElement[], boundValue: Expression, location: TextRange) {
let bindingElements: EffectiveBindingOrAssignmentElement[];
let spreadContainingElements: [Identifier, EffectiveBindingOrAssignmentElement][];
function flattenArrayBindingOrAssignmentPattern(host: FlattenHost, parentElement: BindingOrAssignmentElement, bindingTarget: ArrayBindingOrAssignmentPattern, boundValue: Expression, location: TextRange) {
const elements = getElementsOfBindingOrAssignmentPattern(bindingTarget);
const numElements = elements.length;
if (numElements !== 1 && (host.level < FlattenLevel.ObjectRest || numElements === 0)) {
// For anything other than a single-element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once. Additionally, if we have zero elements
// we need to emit *something* to ensure that in case a 'var' keyword was already emitted,
// so in that case, we'll intentionally create that temporary.
const reuseIdentifierExpressions = !isDeclarationBindingElement(parentElement) || numElements !== 0;
boundValue = ensureIdentifier(host, boundValue, reuseIdentifierExpressions, location);
}
let bindingElements: BindingOrAssignmentElement[];
let restContainingElements: [Identifier, BindingOrAssignmentElement][];
for (let i = 0; i < numElements; i++) {
const element = elements[i];
if (host.level <= FlattenLevel.ObjectRest) {
if (element.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest) && i < numElements - 1) {
if (host.level >= FlattenLevel.ObjectRest) {
// If an array pattern contains an ObjectRest, we must cache the result so that we
// can perform the ObjectRest destructuring in a different declaration
if (element.transformFlags & TransformFlags.ContainsObjectRest) {
const temp = createTempVariable(/*recordTempVariable*/ undefined);
spreadContainingElements = append(spreadContainingElements, <[Identifier, EffectiveBindingOrAssignmentElement]>[temp, element]);
restContainingElements = append(restContainingElements, <[Identifier, BindingOrAssignmentElement]>[temp, element]);
bindingElements = append(bindingElements, host.createArrayBindingOrAssignmentElement(temp));
}
else {
@ -353,21 +302,21 @@ namespace ts {
else if (isOmittedExpression(element)) {
continue;
}
else if (!getEffectiveRestIndicator(element)) {
else if (!getRestIndicatorOfBindingOrAssignmentElement(element)) {
const value = createElementAccess(boundValue, i);
flattenEffectiveBindingElement(host, element, value, /*location*/ element);
flattenBindingOrAssignmentElement(host, element, value, /*location*/ element);
}
else if (i === numElements - 1) {
const value = createArraySlice(boundValue, i);
flattenEffectiveBindingElement(host, element, value, /*location*/ element);
flattenBindingOrAssignmentElement(host, element, value, /*location*/ element);
}
}
if (bindingElements) {
host.emitBindingOrAssignment(host.createArrayBindingOrAssignmentPattern(bindingElements), boundValue, location, bindingTarget);
}
if (spreadContainingElements) {
for (const [id, element] of spreadContainingElements) {
flattenEffectiveBindingElement(host, element, id, element);
if (restContainingElements) {
for (const [id, element] of restContainingElements) {
flattenBindingOrAssignmentElement(host, element, id, element);
}
}
}
@ -448,321 +397,35 @@ namespace ts {
}
}
/**
* Determines whether the EffectiveBindingElement is a declaration
*/
function isDeclarationBindingElement(bindingElement: EffectiveBindingOrAssignmentElement): bindingElement is VariableDeclaration | ParameterDeclaration | BindingElement {
switch (bindingElement.kind) {
case SyntaxKind.VariableDeclaration:
case SyntaxKind.Parameter:
case SyntaxKind.BindingElement:
return true;
}
return false;
}
/**
* Gets the initializer of an EffectiveBindingElement.
*/
function getInitializerOfEffectiveBindingElement(bindingElement: EffectiveBindingOrAssignmentElement): Expression | undefined {
if (isDeclarationBindingElement(bindingElement)) {
// `1` in `let { a = 1 } = ...`
// `1` in `let { a: b = 1 } = ...`
// `1` in `let { a: {b} = 1 } = ...`
// `1` in `let { a: [b] = 1 } = ...`
// `1` in `let [a = 1] = ...`
// `1` in `let [{a} = 1] = ...`
// `1` in `let [[a] = 1] = ...`
return bindingElement.initializer;
}
if (isPropertyAssignment(bindingElement)) {
// `1` in `({ a: b = 1 } = ...)`
// `1` in `({ a: {b} = 1 } = ...)`
// `1` in `({ a: [b] = 1 } = ...)`
return isAssignmentExpression(bindingElement.initializer, /*excludeCompoundAssignment*/ true)
? bindingElement.initializer.right
: undefined;
}
if (isShorthandPropertyAssignment(bindingElement)) {
// `1` in `({ a = 1 } = ...)`
return bindingElement.objectAssignmentInitializer;
}
if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) {
// `1` in `[a = 1] = ...`
// `1` in `[{a} = 1] = ...`
// `1` in `[[a] = 1] = ...`
return bindingElement.right;
}
if (isSpreadExpression(bindingElement) || isPartiallyEmittedExpression(bindingElement)) {
// Recovery consistent with existing emit.
return getInitializerOfEffectiveBindingElement(bindingElement.expression);
}
}
/**
* Gets the name of an EffectiveBindingElement.
*/
function getTargetOfEffectiveBindingElement(bindingElement: EffectiveBindingOrAssignmentElement): EffectiveBindingOrAssignmentTarget {
if (isDeclarationBindingElement(bindingElement)) {
// `a` in `let { a } = ...`
// `a` in `let { a = 1 } = ...`
// `b` in `let { a: b } = ...`
// `b` in `let { a: b = 1 } = ...`
// `a` in `let { ...a } = ...`
// `{b}` in `let { a: {b} } = ...`
// `{b}` in `let { a: {b} = 1 } = ...`
// `[b]` in `let { a: [b] } = ...`
// `[b]` in `let { a: [b] = 1 } = ...`
// `a` in `let [a] = ...`
// `a` in `let [a = 1] = ...`
// `a` in `let [...a] = ...`
// `{a}` in `let [{a}] = ...`
// `{a}` in `let [{a} = 1] = ...`
// `[a]` in `let [[a]] = ...`
// `[a]` in `let [[a] = 1] = ...`
return <ObjectBindingPattern | ArrayBindingPattern | Identifier>bindingElement.name;
}
if (isObjectLiteralElementLike(bindingElement)) {
switch (bindingElement.kind) {
case SyntaxKind.PropertyAssignment:
// `b` in `({ a: b } = ...)`
// `b` in `({ a: b = 1 } = ...)`
// `{b}` in `({ a: {b} } = ...)`
// `{b}` in `({ a: {b} = 1 } = ...)`
// `[b]` in `({ a: [b] } = ...)`
// `[b]` in `({ a: [b] = 1 } = ...)`
// `b.c` in `({ a: b.c } = ...)`
// `b.c` in `({ a: b.c = 1 } = ...)`
// `b[0]` in `({ a: b[0] } = ...)`
// `b[0]` in `({ a: b[0] = 1 } = ...)`
return getTargetOfEffectiveBindingElement(bindingElement.initializer);
case SyntaxKind.ShorthandPropertyAssignment:
// `a` in `({ a } = ...)`
// `a` in `({ a = 1 } = ...)`
return bindingElement.name;
case SyntaxKind.SpreadAssignment:
// `a` in `({ ...a } = ...)`
return getTargetOfEffectiveBindingElement(bindingElement.expression);
}
// no target
return undefined;
}
if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) {
// `a` in `[a = 1] = ...`
// `{a}` in `[{a} = 1] = ...`
// `[a]` in `[[a] = 1] = ...`
// `a.b` in `[a.b = 1] = ...`
// `a[0]` in `[a[0] = 1] = ...`
return getTargetOfEffectiveBindingElement(bindingElement.left);
}
if (isSpreadExpression(bindingElement) || isPartiallyEmittedExpression(bindingElement)) {
// `a` in `[...a] = ...`
return getTargetOfEffectiveBindingElement(bindingElement.expression);
}
// `a` in `[a] = ...`
// `{a}` in `[{a}] = ...`
// `[a]` in `[[a]] = ...`
// `a.b` in `[a.b] = ...`
// `a[0]` in `[a[0]] = ...`
return bindingElement;
}
/**
* Determines whether an EffectiveBindingElement is a rest element.
*/
function getEffectiveRestIndicator(bindingElement: EffectiveBindingOrAssignmentElement): EffectiveBindingOrAssignmentRestIndicator {
switch (bindingElement.kind) {
case SyntaxKind.Parameter:
case SyntaxKind.BindingElement:
// `...` in `let [...a] = ...`
return (<ParameterDeclaration | BindingElement>bindingElement).dotDotDotToken;
case SyntaxKind.SpreadElement:
case SyntaxKind.SpreadAssignment:
// `...` in `[...a] = ...`
return <SpreadElement | SpreadAssignment>bindingElement;
}
return undefined;
}
/**
* Gets the property name of a BindingElement-like element
*/
function getEffectivePropertyNameOfEffectiveBindingElement(bindingElement: EffectiveBindingOrAssignmentElement) {
switch (bindingElement.kind) {
case SyntaxKind.BindingElement:
// `a` in `let { a: b } = ...`
// `[a]` in `let { [a]: b } = ...`
// `"a"` in `let { "a": b } = ...`
// `1` in `let { 1: b } = ...`
if ((<BindingElement>bindingElement).propertyName) {
return (<BindingElement>bindingElement).propertyName;
}
break;
case SyntaxKind.PropertyAssignment:
// `a` in `({ a: b } = ...)`
// `[a]` in `({ [a]: b } = ...)`
// `"a"` in `({ "a": b } = ...)`
// `1` in `({ 1: b } = ...)`
if ((<PropertyAssignment>bindingElement).name) {
return (<PropertyAssignment>bindingElement).name;
}
break;
case SyntaxKind.SpreadAssignment:
// `a` in `({ ...a } = ...)`
return (<SpreadAssignment>bindingElement).name;
}
const target = getTargetOfEffectiveBindingElement(bindingElement);
if (target && isPropertyName(target)) {
return target;
}
Debug.fail("Invalid property name for binding element.");
}
/**
* Determines whether a node is BindingPattern-like
*/
function isEffectiveBindingPattern(node: EffectiveBindingOrAssignmentTarget): node is EffectiveBindingOrAssignmentPattern {
return isEffectiveObjectBindingPattern(node)
|| isEffectiveArrayBindingPattern(node);
}
/**
* Determines whether a node is ObjectBindingPattern-like
*/
function isEffectiveObjectBindingPattern(node: EffectiveBindingOrAssignmentTarget): node is EffectiveObjectBindingOrAssignmentPattern {
switch (node.kind) {
case SyntaxKind.ObjectBindingPattern:
case SyntaxKind.ObjectLiteralExpression:
return true;
}
return false;
}
/**
* Determines whether a node is ArrayBindingPattern-like
*/
function isEffectiveArrayBindingPattern(node: EffectiveBindingOrAssignmentTarget): node is EffectiveArrayBindingOrAssignmentPattern {
switch (node.kind) {
case SyntaxKind.ArrayBindingPattern:
case SyntaxKind.ArrayLiteralExpression:
return true;
}
return false;
}
/**
* Gets the elements of a BindingPattern-like name
*/
function getElementsOfEffectiveBindingPattern(name: EffectiveBindingOrAssignmentPattern): EffectiveBindingOrAssignmentElement[] {
switch (name.kind) {
case SyntaxKind.ObjectBindingPattern:
case SyntaxKind.ArrayBindingPattern:
case SyntaxKind.ArrayLiteralExpression:
// `a` in `{a}`
// `a` in `[a]`
return name.elements;
case SyntaxKind.ObjectLiteralExpression:
// `a` in `{a}`
return name.properties;
}
}
function createEffectiveArrayBindingPattern(elements: EffectiveBindingOrAssignmentElement[]) {
function makeArrayBindingPattern(elements: BindingOrAssignmentElement[]) {
Debug.assertEachNode(elements, isArrayBindingElement);
return createArrayBindingPattern(<ArrayBindingElement[]>elements);
}
function createEffectiveArrayAssignmentPattern(elements: EffectiveBindingOrAssignmentElement[]) {
return createArrayLiteral(map(elements, convertToArrayLiteralElement));
function makeArrayAssignmentPattern(elements: BindingOrAssignmentElement[]) {
return createArrayLiteral(map(elements, convertToArrayAssignmentElement));
}
function createEffectiveObjectBindingPattern(elements: EffectiveBindingOrAssignmentElement[]) {
function makeObjectBindingPattern(elements: BindingOrAssignmentElement[]) {
Debug.assertEachNode(elements, isBindingElement);
return createObjectBindingPattern(<BindingElement[]>elements);
}
function createEffectiveObjectAssignmentPattern(elements: EffectiveBindingOrAssignmentElement[]) {
return createObjectLiteral(map(elements, convertToObjectLiteralElement));
function makeObjectAssignmentPattern(elements: BindingOrAssignmentElement[]) {
return createObjectLiteral(map(elements, convertToObjectAssignmentElement));
}
function createEffectiveBindingElement(name: Identifier) {
function makeBindingElement(name: Identifier) {
return createBindingElement(/*propertyName*/ undefined, /*dotDotDotToken*/ undefined, name);
}
function createEffectiveAssignmentElement(name: Identifier) {
function makeAssignmentElement(name: Identifier) {
return name;
}
function convertToArrayLiteralElement(element: EffectiveBindingOrAssignmentElement) {
if (isBindingElement(element)) {
if (element.dotDotDotToken) {
Debug.assertNode(element.name, isIdentifier);
return setOriginalNode(createSpread(<Identifier>element.name, element), element);
}
const expression = convertToExpressionTarget(<ObjectBindingPattern | ArrayBindingPattern | Identifier>element.name);
return element.initializer ? setOriginalNode(createAssignment(expression, element.initializer, element), element) : expression;
}
Debug.assertNode(element, isExpression);
return <Expression>element;
}
function convertToObjectLiteralElement(element: EffectiveBindingOrAssignmentElement) {
if (isBindingElement(element)) {
if (element.dotDotDotToken) {
Debug.assertNode(element.name, isIdentifier);
return setOriginalNode(createSpreadAssignment(<Identifier>element.name, element), element);
}
if (element.propertyName) {
const expression = convertToExpressionTarget(<ObjectBindingPattern | ArrayBindingPattern | Identifier>element.name);
return setOriginalNode(createPropertyAssignment(element.propertyName, element.initializer ? createAssignment(expression, element.initializer) : expression, element), element);
}
Debug.assertNode(element.name, isIdentifier);
return setOriginalNode(createShorthandPropertyAssignment(<Identifier>element.name, element.initializer, element), element);
}
Debug.assertNode(element, isObjectLiteralElementLike);
return <ObjectLiteralElementLike>element;
}
function convertToExpressionTarget(target: EffectiveBindingOrAssignmentTarget): Expression {
if (isBindingPattern(target)) {
switch (target.kind) {
case SyntaxKind.ArrayBindingPattern:
return setOriginalNode(createArrayLiteral(map(target.elements, convertToArrayLiteralElement), target), target);
case SyntaxKind.ObjectBindingPattern:
return setOriginalNode(createObjectLiteral(map(target.elements, convertToObjectLiteralElement), target), target);
}
return;
}
Debug.assertNode(target, isExpression);
return <Expression>target;
}
/** Given value: o, propName: p, pattern: { a, b, ...p } from the original statement
* `{ a, b, ...p } = o`, create `p = __rest(o, ["a", "b"]);`*/
function createRestCall(value: Expression, elements: EffectiveBindingOrAssignmentElement[], location: TextRange): Expression {
function createRestCall(value: Expression, elements: BindingOrAssignmentElement[], location: TextRange): Expression {
const propertyNames: LiteralExpression[] = [];
for (let i = 0; i < elements.length - 1; i++) {
if (isOmittedExpression(elements[i])) {
@ -771,7 +434,7 @@ namespace ts {
const str = <StringLiteral>createSynthesizedNode(SyntaxKind.StringLiteral);
str.pos = location.pos;
str.end = location.end;
str.text = getTextOfPropertyName(getEffectivePropertyNameOfEffectiveBindingElement(elements[i]));
str.text = getTextOfPropertyName(getPropertyNameOfBindingOrAssignmentElement(elements[i]));
propertyNames.push(str);
}
const args = createSynthesizedNodeArray([value, createArrayLiteral(propertyNames, location)]);

View File

@ -1365,7 +1365,7 @@ namespace ts {
function visitBinaryExpression(node: BinaryExpression, needsDestructuringValue: boolean): Expression {
// If we are here it is because this is a destructuring assignment.
Debug.assert(isDestructuringAssignment(node));
return flattenDestructuringToExpression(
return flattenDestructuringAssignment(
context,
<DestructuringAssignment>node,
needsDestructuringValue,
@ -1383,7 +1383,7 @@ namespace ts {
if (decl.initializer) {
let assignment: Expression;
if (isBindingPattern(decl.name)) {
assignment = flattenDestructuringToExpression(
assignment = flattenDestructuringAssignment(
context,
decl,
/*needsValue*/ false,
@ -1543,8 +1543,7 @@ namespace ts {
if (isBindingPattern(node.name)) {
const recordTempVariablesInLine = !enclosingVariableStatement
|| !hasModifier(enclosingVariableStatement, ModifierFlags.Export);
debugger;
return flattenDestructuringToDeclarations(
return flattenDestructuringBinding(
context,
node,
/*value*/ undefined,
@ -2181,7 +2180,7 @@ namespace ts {
const temp = createTempVariable(undefined);
const newVariableDeclaration = createVariableDeclaration(temp, undefined, undefined, node.variableDeclaration);
const vars = flattenDestructuringToDeclarations(context, node.variableDeclaration, temp, /*skipInitializer*/ false, /*recordTempVariablesInLine*/ true, FlattenLevel.All, visitor);
const vars = flattenDestructuringBinding(context, node.variableDeclaration, temp, /*skipInitializer*/ false, /*recordTempVariablesInLine*/ true, FlattenLevel.All, visitor);
const list = createVariableDeclarationList(vars, /*location*/node.variableDeclaration, /*flags*/node.variableDeclaration.flags);
const destructure = createVariableStatement(undefined, list);

View File

@ -135,7 +135,7 @@ namespace ts {
*/
function visitBinaryExpression(node: BinaryExpression, needsDestructuringValue: boolean): Expression {
if (isDestructuringAssignment(node) && node.left.transformFlags & TransformFlags.ContainsESNext) {
return flattenDestructuringToExpression(
return flattenDestructuringAssignment(
context,
node,
needsDestructuringValue,
@ -156,7 +156,7 @@ namespace ts {
function visitVariableDeclaration(node: VariableDeclaration): VisitResult<VariableDeclaration> {
// If we are here it is because the name contains a binding pattern with a rest somewhere in it.
if (isBindingPattern(node.name) && node.name.transformFlags & TransformFlags.AssertESNext) {
return flattenDestructuringToDeclarations(context, node, /*boundValue*/ undefined, /*skipInitializer*/ false, /*recordTempVariablesInLine*/ true, FlattenLevel.ObjectRest, visitor);
return flattenDestructuringBinding(context, node, /*boundValue*/ undefined, /*skipInitializer*/ false, /*recordTempVariablesInLine*/ true, FlattenLevel.ObjectRest, visitor);
}
return visitEachChild(node, visitor, context);
}
@ -202,14 +202,14 @@ namespace ts {
const declaration = firstOrUndefined(initializer.declarations);
return declaration && declaration.name &&
declaration.name.kind === SyntaxKind.ObjectBindingPattern &&
!!(declaration.name.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest));
!!(declaration.name.transformFlags & TransformFlags.ContainsObjectRest);
}
return false;
}
function isRestAssignment(initializer: ForInitializer) {
return initializer.kind === SyntaxKind.ObjectLiteralExpression &&
initializer.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest);
initializer.transformFlags & TransformFlags.ContainsObjectRest;
}
function visitParameter(node: ParameterDeclaration): ParameterDeclaration {
@ -238,7 +238,7 @@ namespace ts {
function isObjectRestParameter(node: ParameterDeclaration) {
return node.name &&
node.name.kind === SyntaxKind.ObjectBindingPattern &&
!!(node.name.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest));
!!(node.name.transformFlags & TransformFlags.ContainsObjectRest);
}
function visitFunctionDeclaration(node: FunctionDeclaration): FunctionDeclaration {

View File

@ -756,7 +756,7 @@ namespace ts {
*/
function transformInitializedVariable(node: VariableDeclaration): Expression {
if (isBindingPattern(node.name)) {
return flattenDestructuringToExpression(
return flattenDestructuringAssignment(
context,
node,
/*needsValue*/ false,

View File

@ -808,7 +808,7 @@ namespace ts {
function transformInitializedVariable(node: VariableDeclaration, isExportedDeclaration: boolean): Expression {
const createAssignment = isExportedDeclaration ? createExportedVariableAssignment : createNonExportedVariableAssignment;
return isBindingPattern(node.name)
? flattenDestructuringToExpression(context, node, /*needsValue*/ false, FlattenLevel.All, createAssignment, destructuringVisitor)
? flattenDestructuringAssignment(context, node, /*needsValue*/ false, FlattenLevel.All, createAssignment, destructuringVisitor)
: createAssignment(node.name, visitNode(node.initializer, destructuringVisitor, isExpression));
}
@ -1459,7 +1459,7 @@ namespace ts {
*/
function visitDestructuringAssignment(node: DestructuringAssignment): VisitResult<Expression> {
if (hasExportedReferenceInDestructuringTarget(node.left)) {
return flattenDestructuringToExpression(
return flattenDestructuringAssignment(
context,
node,
/*needsValue*/ true,

View File

@ -2353,7 +2353,7 @@ namespace ts {
function transformInitializedVariable(node: VariableDeclaration): Expression {
const name = node.name;
if (isBindingPattern(name)) {
return flattenDestructuringToExpression(
return flattenDestructuringAssignment(
context,
node,
/*needsValue*/ false,

View File

@ -722,22 +722,20 @@ namespace ts {
name: PropertyName;
}
export interface BindingPattern extends Node {
elements: NodeArray<BindingElement | ArrayBindingElement>;
}
export interface ObjectBindingPattern extends BindingPattern {
export interface ObjectBindingPattern extends Node {
kind: SyntaxKind.ObjectBindingPattern;
elements: NodeArray<BindingElement>;
}
export type ArrayBindingElement = BindingElement | OmittedExpression;
export interface ArrayBindingPattern extends BindingPattern {
export interface ArrayBindingPattern extends Node {
kind: SyntaxKind.ArrayBindingPattern;
elements: NodeArray<ArrayBindingElement>;
}
export type BindingPattern = (ObjectBindingPattern | ArrayBindingPattern) & { elements: NodeArray<ArrayBindingElement>; };
export type ArrayBindingElement = BindingElement | OmittedExpression;
/**
* Several node kinds share function-like features such as a signature,
* a name, and a body. These nodes should extend FunctionLikeDeclaration.
@ -1191,7 +1189,49 @@ namespace ts {
left: ArrayLiteralExpression;
}
export type DestructuringAssignment = ObjectDestructuringAssignment | ArrayDestructuringAssignment;
export type DestructuringAssignment
= ObjectDestructuringAssignment
| ArrayDestructuringAssignment
;
export type BindingOrAssignmentElement
= VariableDeclaration
| ParameterDeclaration
| BindingElement
| PropertyAssignment // AssignmentProperty
| ShorthandPropertyAssignment // AssignmentProperty
| SpreadAssignment // AssignmentRestProperty
| OmittedExpression // Elision
| SpreadElement // AssignmentRestElement
| ArrayLiteralExpression // ArrayAssignmentPattern
| ObjectLiteralExpression // ObjectAssignmentPattern
| AssignmentExpression<EqualsToken> // AssignmentElement
| Identifier // DestructuringAssignmentTarget
| PropertyAccessExpression // DestructuringAssignmentTarget
| ElementAccessExpression // DestructuringAssignmentTarget
;
export type BindingOrAssignmentElementRestIndicator
= DotDotDotToken // from BindingElement
| SpreadElement // AssignmentRestElement
| SpreadAssignment // AssignmentRestProperty
;
export type BindingOrAssignmentElementTarget = BindingOrAssignmentPattern | Expression;
export type ObjectBindingOrAssignmentPattern
= ObjectBindingPattern
| ObjectLiteralExpression // ObjectAssignmentPattern
;
export type ArrayBindingOrAssignmentPattern
= ArrayBindingPattern
| ArrayLiteralExpression // ArrayAssignmentPattern
;
export type AssignmentPattern = ObjectLiteralExpression | ArrayLiteralExpression;
export type BindingOrAssignmentPattern = ObjectBindingOrAssignmentPattern | ArrayBindingOrAssignmentPattern;
export interface ConditionalExpression extends Expression {
kind: SyntaxKind.ConditionalExpression;
@ -3554,10 +3594,10 @@ namespace ts {
TypeExcludes = ~ContainsTypeScript,
ObjectLiteralExcludes = NodeExcludes | ContainsDecorators | ContainsComputedPropertyName | ContainsLexicalThisInComputedPropertyName | ContainsObjectSpread,
ArrayLiteralOrCallOrNewExcludes = NodeExcludes | ContainsSpread,
VariableDeclarationListExcludes = NodeExcludes | ContainsBindingPattern | ContainsObjectSpread,
VariableDeclarationListExcludes = NodeExcludes | ContainsBindingPattern | ContainsObjectRest,
ParameterExcludes = NodeExcludes,
CatchClauseExcludes = NodeExcludes | ContainsObjectSpread,
BindingPatternExcludes = NodeExcludes | ContainsSpread,
CatchClauseExcludes = NodeExcludes | ContainsObjectRest,
BindingPatternExcludes = NodeExcludes | ContainsRest,
// Masks
// - Additional bitmasks

View File

@ -3948,6 +3948,78 @@ namespace ts {
|| kind === SyntaxKind.OmittedExpression;
}
/**
* Determines whether the BindingOrAssignmentElement is a BindingElement-like declaration
*/
export function isDeclarationBindingElement(bindingElement: BindingOrAssignmentElement): bindingElement is VariableDeclaration | ParameterDeclaration | BindingElement {
switch (bindingElement.kind) {
case SyntaxKind.VariableDeclaration:
case SyntaxKind.Parameter:
case SyntaxKind.BindingElement:
return true;
}
return false;
}
/**
* Determines whether a node is a BindingOrAssignmentElement
*/
export function isBindingOrAssignmentElement(node: Node): node is BindingOrAssignmentElement {
switch (node.kind) {
case SyntaxKind.VariableDeclaration:
case SyntaxKind.Parameter:
case SyntaxKind.BindingElement:
case SyntaxKind.PropertyAssignment:
case SyntaxKind.ShorthandPropertyAssignment:
case SyntaxKind.SpreadAssignment:
case SyntaxKind.OmittedExpression:
case SyntaxKind.ArrayLiteralExpression:
case SyntaxKind.ObjectLiteralExpression:
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
case SyntaxKind.Identifier:
case SyntaxKind.SpreadElement:
return true;
}
return isAssignmentExpression(node, /*excludeCompoundAssignment*/ true);
}
/**
* Determines whether a node is a BindingOrAssignmentPattern
*/
export function isBindingOrAssignmentPattern(node: BindingOrAssignmentElementTarget): node is BindingOrAssignmentPattern {
return isObjectBindingOrAssignmentPattern(node)
|| isArrayBindingOrAssignmentPattern(node);
}
/**
* Determines whether a node is an ObjectBindingOrAssignmentPattern
*/
export function isObjectBindingOrAssignmentPattern(node: BindingOrAssignmentElementTarget): node is ObjectBindingOrAssignmentPattern {
switch (node.kind) {
case SyntaxKind.ObjectBindingPattern:
case SyntaxKind.ObjectLiteralExpression:
return true;
}
return false;
}
/**
* Determines whether a node is an ArrayBindingOrAssignmentPattern
*/
export function isArrayBindingOrAssignmentPattern(node: BindingOrAssignmentElementTarget): node is ArrayBindingOrAssignmentPattern {
switch (node.kind) {
case SyntaxKind.ArrayBindingPattern:
case SyntaxKind.ArrayLiteralExpression:
return true;
}
return false;
}
// Expression
export function isArrayLiteralExpression(node: Node): node is ArrayLiteralExpression {