mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-24 17:18:56 -05:00
Fix destructuring evaluation order for initializers
This commit is contained in:
@@ -5,6 +5,7 @@ namespace ts {
|
||||
level: FlattenLevel;
|
||||
downlevelIteration: boolean;
|
||||
hoistTempVariables: boolean;
|
||||
hasTransformedPriorElement?: boolean; // indicates whether we've transformed a prior declaration
|
||||
emitExpression: (value: Expression) => void;
|
||||
emitBindingOrAssignment: (target: BindingOrAssignmentElementTarget, value: Expression, location: TextRange, original: Node | undefined) => void;
|
||||
createArrayBindingOrAssignmentPattern: (elements: BindingOrAssignmentElement[]) => ArrayBindingOrAssignmentPattern;
|
||||
@@ -265,18 +266,27 @@ namespace ts {
|
||||
value: Expression | undefined,
|
||||
location: TextRange,
|
||||
skipInitializer?: boolean) {
|
||||
const bindingTarget = getTargetOfBindingOrAssignmentElement(element)!; // TODO: GH#18217
|
||||
if (!skipInitializer) {
|
||||
const initializer = visitNode(getInitializerOfBindingOrAssignmentElement(element), flattenContext.visitor, isExpression);
|
||||
if (initializer) {
|
||||
// Combine value and initializer
|
||||
value = value ? createDefaultValueCheck(flattenContext, value, initializer, location) : initializer;
|
||||
if (value) {
|
||||
value = createDefaultValueCheck(flattenContext, value, initializer, location);
|
||||
// If 'value' is not a simple expression, it could contain side-effecting code that should evaluate before an object or array binding pattern.
|
||||
if (!isSimpleInlineableExpression(initializer) && isBindingOrAssignmentPattern(bindingTarget)) {
|
||||
value = ensureIdentifier(flattenContext, value, /*reuseIdentifierExpressions*/ true, location);
|
||||
}
|
||||
}
|
||||
else {
|
||||
value = initializer;
|
||||
}
|
||||
}
|
||||
else if (!value) {
|
||||
// Use 'void 0' in absence of value and initializer
|
||||
value = flattenContext.context.factory.createVoidZero();
|
||||
}
|
||||
}
|
||||
const bindingTarget = getTargetOfBindingOrAssignmentElement(element)!; // TODO: GH#18217
|
||||
if (isObjectBindingOrAssignmentPattern(bindingTarget)) {
|
||||
flattenObjectBindingOrAssignmentPattern(flattenContext, element, bindingTarget, value!, location);
|
||||
}
|
||||
@@ -393,7 +403,8 @@ namespace ts {
|
||||
if (flattenContext.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.ContainsObjectRestOrSpread) {
|
||||
if (element.transformFlags & TransformFlags.ContainsObjectRestOrSpread || flattenContext.hasTransformedPriorElement && !isSimpleBindingOrAssignmentElement(element)) {
|
||||
flattenContext.hasTransformedPriorElement = true;
|
||||
const temp = flattenContext.context.factory.createTempVariable(/*recordTempVariable*/ undefined);
|
||||
if (flattenContext.hoistTempVariables) {
|
||||
flattenContext.context.hoistVariableDeclaration(temp);
|
||||
@@ -428,6 +439,17 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function isSimpleBindingOrAssignmentElement(element: BindingOrAssignmentElement): boolean {
|
||||
const target = getTargetOfBindingOrAssignmentElement(element);
|
||||
if (!target || isOmittedExpression(target)) return true;
|
||||
const propertyName = tryGetPropertyNameOfBindingOrAssignmentElement(element);
|
||||
if (propertyName && !isPropertyNameLiteral(propertyName)) return false;
|
||||
const initializer = getInitializerOfBindingOrAssignmentElement(element);
|
||||
if (initializer && !isSimpleInlineableExpression(initializer)) return false;
|
||||
if (isBindingOrAssignmentPattern(target)) return every(getElementsOfBindingOrAssignmentPattern(target), isSimpleBindingOrAssignmentElement);
|
||||
return isIdentifier(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an expression used to provide a default value if a value is `undefined` at runtime.
|
||||
*
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
"unittests/evaluation/asyncArrow.ts",
|
||||
"unittests/evaluation/asyncGenerator.ts",
|
||||
"unittests/evaluation/awaiter.ts",
|
||||
"unittests/evaluation/destructuring.ts",
|
||||
"unittests/evaluation/forAwaitOf.ts",
|
||||
"unittests/evaluation/forOf.ts",
|
||||
"unittests/evaluation/optionalCall.ts",
|
||||
|
||||
39
src/testRunner/unittests/evaluation/destructuring.ts
Normal file
39
src/testRunner/unittests/evaluation/destructuring.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
describe("unittests:: evaluation:: destructuring", () => {
|
||||
// https://github.com/microsoft/TypeScript/issues/39205
|
||||
describe("correct order for array destructuring evaluation and initializers", () => {
|
||||
it("when element is undefined", () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
export const output: any[] = [];
|
||||
const order = (n: any): any => output.push(n);
|
||||
let [{ [order(1)]: x } = order(0)] = [];
|
||||
`, { target: ts.ScriptTarget.ES5 });
|
||||
assert.deepEqual(result.output, [0, 1]);
|
||||
});
|
||||
it("when element is defined", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
export const output: any[] = [];
|
||||
const order = (n: any): any => output.push(n);
|
||||
let [{ [order(1)]: x } = order(0)] = [{}];
|
||||
`, { target: ts.ScriptTarget.ES5 });
|
||||
assert.deepEqual(result.output, [1]);
|
||||
});
|
||||
});
|
||||
describe("correct order for array destructuring evaluation and initializers with spread", () => {
|
||||
it("ES5", () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
export const output: any[] = [];
|
||||
const order = (n: any): any => output.push(n);
|
||||
let { [order(0)]: { [order(2)]: z } = order(1), ...w } = {} as any;
|
||||
`, { target: ts.ScriptTarget.ES5 });
|
||||
assert.deepEqual(result.output, [0, 1, 2]);
|
||||
});
|
||||
it("ES2015", () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
export const output: any[] = [];
|
||||
const order = (n: any): any => output.push(n);
|
||||
let { [order(0)]: { [order(2)]: z } = order(1), ...w } = {} as any;
|
||||
`, { target: ts.ScriptTarget.ES2015 });
|
||||
assert.deepEqual(result.output, [0, 1, 2]);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user