October 22, 2020
TestsSource
- Optional chaining (`?.` operator)
- Whats the default return value if an element is undefined/null? (without the operator)
- What about newlines, spaces?
- How does it work with (async, generators) functions?
Source Code
The source code we wrote during the event.
optional-chaining.spec.js
import assert from 'assert';
describe('Optional chaining (`?.` operator)', () => {
describe('Whats the default return value if an element is undefined/null? (without the operator)', () => {
it('applying the operator ?. on undefined returns undefined', () => {
assert.strictEqual(undefined?.foo, undefined);
});
it('applying the operator on null returns undefined', () => {
assert.strictEqual(null?.foo, undefined);
});
it('calling `undefined?.foo()` returns undefined', () => {
assert.strictEqual(undefined?.foo(), undefined);
});
it('an expression in a fn parameter is NOT evaluated before option chaining is applied', () => {
let i = 0;
undefined?.foo(i++);
assert.strictEqual(i, 0);
});
it('the expression `++i` as a fn parameter is NOT evaluated before optional chaining is applied', () => {
let i = 0;
undefined?.foo(++i);
assert.strictEqual(i, 0);
});
it('the expression `++i` is not evaluated before optional chaining', () => {
let i = 0;
undefined?.[++i];
assert.strictEqual(i, 0);
});
it('does NOT throw an error as an dynamic accessor is NOT evaluated', () => {
assert.doesNotThrow(() => {
undefined?.[(() => { throw Error(); })()];
});
});
});
describe('What about newlines, spaces?', () => {
it('newline before `?.` works', () => {
const x = {foo: 1};
assert.doesNotThrow(() => eval(`
x
?.foo;
`));
assert.strictEqual(x
?.foo, 1
)
});
it('newline after `?.` works', () => {
const x = {foo: 1};
assert.doesNotThrow(() => eval(`
x?.
foo;
`));
});
it('when we split the operator with a newline, then it is invalid syntax', () => {
const x = {foo: 1};
assert.throws(() => eval(`
x?
.foo
`), SyntaxError);
});
it('when we split the operator with a space, then it is not valid syntax', () => {
assert.throws(() => eval(`x? .foo`), SyntaxError);
});
});
describe('How does it work with (async, generators) functions?', () => {
it('use async fn as accessor, which returns undefined, will result in undefined', async () => {
const fn = async () => 'a';
const x = {};
assert.strictEqual(x?.[await fn()], undefined);
});
it('async fn call on the left hand side can be optional chained', async () => {
const fn = async () => ({foo: 42});
assert.strictEqual(await fn()?.foo, undefined);
assert.strictEqual((await fn())?.foo, 42);
const fn1 = () => ({foo: Promise.resolve(42)});
assert.strictEqual(await fn1()?.foo, 42);
});
});
});