-
Notifications
You must be signed in to change notification settings - Fork 27
Description
Why does the following work:
[1, 2].map(function(x) { return x + 1 })
But not the following:
[{a: 1}, {a: 2}].map(function(x) { return x["a"] })
Since .map()
does not seem to be an issue, and member expressions of course aren’t, I’m not sure why this fails. The actual error is TypeError: Cannot read properties of null (reading 'a')
The inner function ASTs are respectively:
ReturnStatement {
type: 'ReturnStatement',
argument: BinaryExpression {
type: 'BinaryExpression',
operator: '+',
left: Identifier { type: 'Identifier', name: 'x' },
right: Literal { type: 'Literal', value: 1, raw: '1' }
}
}
And:
ReturnStatement {
type: 'ReturnStatement',
argument: ComputedMemberExpression {
type: 'MemberExpression',
computed: true,
object: Identifier { type: 'Identifier', name: 'x' },
property: Literal { type: 'Literal', value: 'a', raw: '"a"' }
}
}
Statically evaluating a MemberExpression is not harder than a BinaryExpression, is it?
After looking at the code, it seems to me that in both cases the x
statement is statically evaluated to null
. In the BinaryExpression
code, the binary evaluation returns null + 1
which is 1
, and you then allow and execute the function.
However since null["a"]
raises an error, the function is not allowed and executed. However x["a"]
is knowable statically (it is either the property or undefined
if it’s absent − if you call evaluate('x["b"]', {x: {a: 1}})
, you get undefined
, not an error), so the function should execute in this case as well.
There is a simple workaround which is to return (x || {a: ""})["a"]
instead of x["a"]
, this avoids throwing an error by ensuring there is a fallback value to x.a
if x
is undefined.