[TS] Non-null assertion operator を理解する
Non-null assertion operator とは、オブジェクトのうしろにつける !
のこと。
たとえばこんなもの。TS 2.7 から使える。
object!.open();
TypeScript Deep Dive によれば、
新しい
!
ポストフィックス式演算子を使用して、型チェッカーが結論付けられないコンテキストにおいて、そのオペランドが非 null でかつ非 undefined であることをアサートすることができます。
そしてこうも書いてある。
これは単なるアサーションであり、型アサーションと同じように、あなたは値が null でないことを確認する責任があることに注意してください。 非 null アサーションは、本質的にはコンパイラに”それは null でないことが分かっているから、null ではないものとして使います”と伝えるものです。
なるほど、強制的に null
または undefined
でないことにできてしまうから、自分で気をつけないといけないよと。
一応「アサーション」についても調べてみる。
表明とは、プログラミングにおける概念のひとつであり、そのプログラムの前提条件を示すのに使われる。 アサーションとも呼ばれる。表明は、プログラムのその箇所で必ず真であるべき式の形式をとる。 Wikipedia
ここでは「オブジェクトが null でないこと」を前提条件として表明していることになる。
となると、Non-null assertion とは null
または undefind
でないことを表明することであり、Non-null assertion operator はそのための演算子であるといえるかな。
Deep Dive にはこんなコードも書かれていた。 ここまで調べれば理解できるはず。
// Compiled with --strictNullChecks
function validateEntity(e?: Entity) {
// Throw exception if e is null or invalid entity
}
function processEntity(e?: Entity) {
validateEntity(e);
let a = e.name; // TS ERROR: e may be null.
let b = e!.name; // OKAY. We are asserting that e is non-null.
}
🐈
具体的な使用例をみてみる。この記事を書くきっかけになったのが以下のコード。
単純なリストで、各項目に異なる ref
をわりあてている。
リストの項目をクリックすると ref
を参照してそこまでスクロールするよ、というもの。
handleClick
のなかで Non-null assertion operator が使われている。
const refs = data.map(_ => React.createRef<HTMLDivElement>());
const handleClick = (key: number) => {
refs[key]!.current!.scrollIntoView({
behavior: "smooth",
block: "start"
});
};
...中略...
return (
<List>
data.map((v, i) => {
return (
<ListItem
ref={refs[key]}
onClick={handleClick(i)}
/>
);
}
</List>
);
ref
には current
というプロパティがあって、それをもとにスクロールする位置を決める。
しかし current
の型は HTMLDivElement | null
なので、たとえすべてのリスト項目に ref
を指定して null
になる可能性をなくしたとしても、型として null
が含まれてしまう。
そうなると困るのが ref.current
を使うとき。以下のように書くと怒られる。
refs[key].current.scrollIntoView();
// Object is possiblly 'null'
それもそのはず、型として null
が含まれているから。
ちなみに、これは undefined
でも同じことになる。
じゃあどうするかというと、ここは null
の可能性も undefined
の可能性もありませんよ!と明示する。
そのために使うのが Non-null assertion operator 。
refs[key].current!.scrollIntoView();