fix(compiler): unable to resolve destructuring variable declarations (#37497)
Currently the partial evaluator isn't able to resolve a variable declaration that uses destructuring in the form of `const {value} = {value: 0}; const foo = value;`. These changes add some logic to allow for us to resolve the variable's value. Fixes #36917. PR Close #37497
This commit is contained in:
parent
12f2101d08
commit
2e355fd4d3
|
@ -266,6 +266,8 @@ export class StaticInterpreter {
|
|||
return this.visitEnumDeclaration(node, context);
|
||||
} else if (ts.isSourceFile(node)) {
|
||||
return this.visitSourceFile(node, context);
|
||||
} else if (ts.isBindingElement(node)) {
|
||||
return this.visitBindingElement(node, context);
|
||||
} else {
|
||||
return this.getReference(node, context);
|
||||
}
|
||||
|
@ -347,9 +349,8 @@ export class StaticInterpreter {
|
|||
});
|
||||
}
|
||||
|
||||
private accessHelper(
|
||||
node: ts.Expression, lhs: ResolvedValue, rhs: string|number,
|
||||
context: Context): ResolvedValue {
|
||||
private accessHelper(node: ts.Node, lhs: ResolvedValue, rhs: string|number, context: Context):
|
||||
ResolvedValue {
|
||||
const strIndex = `${rhs}`;
|
||||
if (lhs instanceof Map) {
|
||||
if (lhs.has(strIndex)) {
|
||||
|
@ -598,6 +599,47 @@ export class StaticInterpreter {
|
|||
}
|
||||
}
|
||||
|
||||
private visitBindingElement(node: ts.BindingElement, context: Context): ResolvedValue {
|
||||
const path: ts.BindingElement[] = [];
|
||||
let closestDeclaration: ts.Node = node;
|
||||
|
||||
while (ts.isBindingElement(closestDeclaration) ||
|
||||
ts.isArrayBindingPattern(closestDeclaration) ||
|
||||
ts.isObjectBindingPattern(closestDeclaration)) {
|
||||
if (ts.isBindingElement(closestDeclaration)) {
|
||||
path.unshift(closestDeclaration);
|
||||
}
|
||||
|
||||
closestDeclaration = closestDeclaration.parent;
|
||||
}
|
||||
|
||||
if (!ts.isVariableDeclaration(closestDeclaration) ||
|
||||
closestDeclaration.initializer === undefined) {
|
||||
return DynamicValue.fromUnknown(node);
|
||||
}
|
||||
|
||||
let value = this.visit(closestDeclaration.initializer, context);
|
||||
for (const element of path) {
|
||||
let key: number|string;
|
||||
if (ts.isArrayBindingPattern(element.parent)) {
|
||||
key = element.parent.elements.indexOf(element);
|
||||
} else {
|
||||
const name = element.propertyName || element.name;
|
||||
if (ts.isIdentifier(name)) {
|
||||
key = name.text;
|
||||
} else {
|
||||
return DynamicValue.fromUnknown(element);
|
||||
}
|
||||
}
|
||||
value = this.accessHelper(element, value, key, context);
|
||||
if (value instanceof DynamicValue) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private stringNameFromPropertyName(node: ts.PropertyName, context: Context): string|undefined {
|
||||
if (ts.isIdentifier(node) || ts.isStringLiteral(node) || ts.isNumericLiteral(node)) {
|
||||
return node.text;
|
||||
|
|
|
@ -200,6 +200,58 @@ runInEachFileSystem(() => {
|
|||
expect(evaluate('const a = null;', 'a')).toEqual(null);
|
||||
});
|
||||
|
||||
it('supports destructuring array variable declarations', () => {
|
||||
const code = `
|
||||
const [a, b, c, d] = [0, 1, 2, 3];
|
||||
const e = c;
|
||||
`;
|
||||
|
||||
expect(evaluate(code, 'a')).toBe(0);
|
||||
expect(evaluate(code, 'b')).toBe(1);
|
||||
expect(evaluate(code, 'c')).toBe(2);
|
||||
expect(evaluate(code, 'd')).toBe(3);
|
||||
expect(evaluate(code, 'e')).toBe(2);
|
||||
});
|
||||
|
||||
it('supports destructuring object variable declaration', () => {
|
||||
const code = `
|
||||
const {a, b, c, d} = {a: 0, b: 1, c: 2, d: 3};
|
||||
const e = c;
|
||||
`;
|
||||
|
||||
expect(evaluate(code, 'a')).toBe(0);
|
||||
expect(evaluate(code, 'b')).toBe(1);
|
||||
expect(evaluate(code, 'c')).toBe(2);
|
||||
expect(evaluate(code, 'd')).toBe(3);
|
||||
expect(evaluate(code, 'e')).toBe(2);
|
||||
});
|
||||
|
||||
it('supports destructuring object variable declaration with an alias', () => {
|
||||
expect(evaluate(`const {a: value} = {a: 5}; const e = value;`, 'e')).toBe(5);
|
||||
});
|
||||
|
||||
it('supports nested destructuring object variable declarations', () => {
|
||||
expect(evaluate(`const {a: {b: {c}}} = {a: {b: {c: 0}}};`, 'c')).toBe(0);
|
||||
});
|
||||
|
||||
it('supports nested destructuring array variable declarations', () => {
|
||||
expect(evaluate(`const [[[a]]] = [[[1]]];`, 'a')).toBe(1);
|
||||
});
|
||||
|
||||
it('supports nested destructuring variable declarations mixing arrays and objects', () => {
|
||||
expect(evaluate(`const {a: {b: [[c]]}} = {a: {b: [[1337]]}};`, 'c')).toBe(1337);
|
||||
});
|
||||
|
||||
it('resolves unknown values in a destructured variable declaration as dynamic values', () => {
|
||||
const value = evaluate(
|
||||
`const {a: {body}} = {a: window};`, 'body',
|
||||
[{name: _('/window.ts'), contents: `declare const window: any;`}]);
|
||||
if (!(value instanceof DynamicValue)) {
|
||||
return fail(`Should have resolved to a DynamicValue`);
|
||||
}
|
||||
expect(value.node.getText()).toBe('body');
|
||||
});
|
||||
|
||||
it('resolves unknown binary operators as dynamic value', () => {
|
||||
const value = evaluate('declare const window: any;', '"location" in window');
|
||||
if (!(value instanceof DynamicValue)) {
|
||||
|
|
Loading…
Reference in New Issue