JavaScriptのvoid演算子を使っていく
技術・テクノロジーJavaScript には void
演算子があります。
void
演算子は与えられた式 (expression
) を評価し、undefined
を返します。
これまでは void
演算子を使うことがあまりなかったのですが、最近使い道が分かってきたため、この記事ではその一例をご紹介します。
アロー関数からのリーク防止
MDN にも例がありますが、void
演算子を使うことによってアロー関数をより安全に使えます。
例えば、関数を引数にとる foo
関数と、実行した際に値を返す bar
関数があったとし、これらを組み合わせて使うケースを考えてみます。
foo
の引数の関数は値を返す必要がないとした場合、一般的には以下のように書くことが多いかと思います。
foo(() => {
bar()
})
ただ、これだと記述が 3 行になってしまうので、以下のようにインラインで書いてしまう人も多いのではないでしょうか?
foo(() => bar())
foo
関数の引数の関数は値を返すことを考慮していないため、今回は問題ないかもしれませんが、返す必要のない値を返しているのは少し気持ち悪いですよね。
このようなケースで void
演算子を使うと、インラインで書きつつ 3 行で書いたときと挙動は変わらないため、安心感があります。
foo(() => void bar())
TypeScript で役に立つケース
最近、型レベルで void
演算子を使うと嬉しいケースがあったのでご紹介します。
react-dom/test-utils
の act
関数は、引数として関数を取りますが、その関数が Promise
を返すかどうかによって挙動が異なります。
以下は執筆時点での act
の型定義です。
declare const UNDEFINED_VOID_ONLY: unique symbol;
// tslint:disable-next-line: void-return
type VoidOrUndefinedOnly = void | { [UNDEFINED_VOID_ONLY]: never };
export function act(callback: () => Promise<void>): Promise<undefined>;
export function act(callback: () => VoidOrUndefinedOnly): void;
これを前提として、以下のような呼び出しをするケースを考えてみます。
await act(async () => {
jest.runAllTimers()
})
これはよくある実装かと思いますが、個人的にはインラインで記述したいと考えました。
await act(async () => jest.runAllTimers())
しかしこれは型エラーになってしまいます。なぜなら、jest.runAllTimers()
は値を返すため、前述した act
の型定義と一致しないからです。
このようなケースで void
演算子を使うと、インラインで記述しつつ型エラーも消せます。
await act(async () => void jest.runAllTimers())
優秀な型定義のおかげで、インラインで書きつつも正しく実装することができて満足です😋
おわりに
数年前までは意図しない副作用が起きることを考慮せずに、アロー関数をインラインで記述することも多々ありました。最近はそういった書き方をしなくなったのですが、行数が増えるため少し気持ち悪いな〜と思っていました。
void
演算子を使うと、この問題にスマートに対処できそうです。これからは積極的に使っていきたいですね。
余談ですが、void
演算子は今回の例以外にも即時実行関数式など便利な使い道があります。気になる方はぜひ MDN を確認してみてください。