What about using dig
Actually, many codebases are already using dig in those cases (and it is clearer without redefinition on nil, telling the reader "we are aware there might be nil here"):
array_or_nil&.dig(index)
...but using the proposed solution is shorter and cleaner.
There is also nothing wrong with array_or_nil && array_or_nil[index].
...until it is part of the message chain:
# bad
calculate_some_value(with, some, arguments) && calculate_some_value(with, some, arguments)[index]
# wordy, requires inventing new names:
intermediate = calculate_some_value(with, some, arguments) 
intermediate && intermediate[index]
Though, even if there is already a variable, foo && foo[bar] is already non-DRY and impends reading.
IMO these ?.[ look too cryptic.
I honestly don't see how it is more cryptic than &.foo(. For the codebases that use &. where appropriate, an attempt to write foo&.[bar] is what less experienced programmers always try to do, and "why it doesn't work" is more cryptic than if it would.
Those are somewhat more confusing, too:
# I tried to use foo&.[bar], it failed, I know [] is a method and I write this:
foo&.[](bar)
# I find the above ugly, and I switch to dig:
foo&.dig(bar)
...but it is kind of non-standar to use dig with one argument, and needs to be remembered as a separate idiom.