TypeScript で querySelector メソッドを使うときに型引数を指定する

こんにちは、エンジニア職の id:nanto_vi です。(この記事は、はてなエンジニア Advent Calendar 2020 の 12 日目の記事です。昨日は id:cohalz さんによる「Webサービスのモニタリングについてのチェックリスト」でした。)

結論

TypeScript で querySelectorquerySelectorAll メソッドを呼び出すときは、型引数を指定しましょう (メソッド名の直後の < > で囲んだ部分です)。

const foo = document.querySelector<HTMLElement>('.foo');
if (foo) {
    foo.style.display = '';
}

const bars = document.querySelectorAll<HTMLInputElement>('input[name="bar"]');
bars.forEach((bar) => {
    console.log(bar.value);
});

解説

querySelector メソッド

皆さんは TypeScript を書いていて、以下のようなエラーに遭遇したことはありませんか?

const foo = document.querySelector('.foo');
if (foo) {
    foo.style.display = '';
    // Property 'style' does not exist on type 'Element'.
}

style プロパティが存在しないと言われています。変数 fooElement 型であり、style プロパティが定義されているのは HTMLElement 型 (Element 型を継承している) なので、型定義上は確かに style プロパティにアクセスできませんね。

では変数 fooHTMLElement 型にすればよいと以下のようなコードを書いてしまうと……

// Bad
const foo = document.querySelector('.foo') as HTMLElement;

これはいけません! これだと Element | null 型から HTMLElement 型にキャストすることになり、nullability が除去されてしまいます。このままコードを書き続けると、要素が見つからないときに実行時エラーを引き起こしかねません。

// Good
const foo = document.querySelector<HTMLElement>('.foo');

上記のように型引数を指定することで、querySelector メソッドの返り値が HTMLElement | null 型となり、style プロパティにもアクセスできるようになります。

「この要素は絶対存在するから nullability を除去したい」というときは後置 ! 演算子を使いましょう。

querySelectorAll メソッド

querySelectorAll メソッドも同様に型引数を指定できます。型引数を指定することで、配列に変換したり forEach メソッドを使ったりしたときに、適切な型が推論されるようになります。

const bars = document.querySelectorAll<HTMLInputElement>('input[name="bar"]');
// ここで bars は NodeListOf<HTMLInputElement> 型
bars.forEach((bar) => {
    // ここで bar は HTMLInputElement 型
    console.log(bar.value);
});

要素名からの型推論

querySelectorquerySelectorAll メソッドでは、引数が要素名そのままのときに適切な型が推論されます。

// ここで anchor は HTMLAnchorElement | null 型
const anchor = document.querySelector('a');

// ここで buttons は NodeListOf<HTMLButtonElement> 型
const buttons = document.querySelectorAll('button');

クラスセレクタなどをつけてしまうと、要素名からの型推論は働きません。

// ここで fooAnchor は Element | null 型
const fooAnchor = document.querySelector('a.foo');

要素と型との対応

a 要素は HTMLAnchorElement 型であるといった対応は、MDN の HTML 要素リファレンスから調べられます。調べたい要素のページに飛び、「DOM インターフェイス」という項目を探してください。

もちろん、おおもとの仕様書である HTML Living Standard にも書かれています。

宣伝

はてなでは、型の整ったコードを書く、型にはまらないエンジニアを募集しています。

明日の Advent Calendar は id:ne-sachirou さんです。