You were close, but you use nesting rather than dot notation:
// Outer −−−−−−−−−−−−−−−−−−−−v−−−−−−−−−−−−−−−−−−−−−−v
let el = scopes.reduce((tot, {actions: {length: len}}) => tot + len, 0);
// Inner −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^−−−−−−−−−−−−^
Live Example:
const scopes = [
{actions: [1, 2, 3]},
{actions: []},
{actions: [4, 5]}
];
let el = scopes.reduce((tot, {actions: {length: len}}) => tot + len, 0);
console.log(el); // 5
The key thing to remember with destructuring is that the syntax is identical to object and array literals, complete with nesting; it's just that the information flow is the other direction. In an object literal, for instance, the data flows right to left, from the source (source
) to the target (prop
):
let source = 42;
let obj = {prop: source};
// <−−−−−*
in destructuring, the data flows left to right, from the source (prop
) to the target (target
):
let {prop: target};
// *−−−−−>
console.log(target); // 42
and the target can be a variable, an object property, or even another destructuring pattern. That's what we're using above: The target of the actions
property is the destructuring pattern {length: len}
, which puts the value of length
into the variable len
. Here's Figure 7-1 from my new book (see my profile for links):

You could also use shorthand notation and use length
in your callback:
let el = scopes.reduce((tot, {actions: {length}}) => tot + length, 0);
Live Example:
const scopes = [
{actions: [1, 2, 3]},
{actions: []},
{actions: [4, 5]}
];
let el = scopes.reduce((tot, {actions: {length}}) => tot + length, 0);
console.log(el); // 5