🎨

TypeScriptでUser-Agent Client Hintsを利用してブラウザ判定する際の注意点

2022/03/26に公開

はじめに

User Agent Client Hints(以下 UA-CH)を使ってブラウザ判定を行なう処理の実装を行なった機会がありましたが、何点か注意した点があったのでまとめました。

UA-CH について

一言でいうと、OS やブラウザの情報を取得する事ができる JavaScript API です。

window.navigator.userAgentData を利用します。

const useAgentData = navigator.userAgentData;
console.log(useAgentData);

上記をコピーしてコンソールで実行すると、

上記の様に OS の情報、mobile:falseといった感じで情報を取得することができます。

余談ですが、非同期で実行することでより詳細な情報を取得することができます。

(async () => {
  const useAgentData = navigator.userAgentData;
  const highEntropyValues = await useAgentData.getHighEntropyValues([
    "platform",
    "platformVersion",
    "architecture",
    "model",
    "uaFullVersion",
  ]);
  console.log(useAgentData);
  console.log("詳細", highEntropyValues);
})();

userAgentData は Chrome ではバージョン 90 から利用できるようになりましたが、以前は UserAgent やブラウザ判定を行なってくれるライブラリ(UAParser とか)によって OS の種類やブラウザのバージョン情報などを取得する方法が一般的でした。

しかし、Chrome では UserAgent に情報の提供が段階的に削減されることが決まっています。

(この辺は詳しく説明している記事が沢山あるので省略します)

注意しないといけない点に絞ると、現時点ではブラウザ(主に safari)によっては、UA-CH を使って取得する事ができないという点です。

その点を意識して実装する上での注意点となります。

実装

注意点 1:型定義ファイルを作成する

TypeScript バージョン 4.5.4 時点ではwindow.navigator.userAgentDataの型定義が組み込まれていない為@typesフォルダ下にwindow.d.tsというファイル名を作成し、型定義を行います。[1]

window.d.ts
type Brand = {
  readonly brand: string;
  readonly version: string;
};

type NavigatorUAData = {
  readonly brands: Brand[];
  readonly mobile: boolean;
  readonly platform: string;
};

interface Navigator {
  userAgentData: NavigatorUAData;
}

interfaceによって、Navigatorの型がマージされてuserAgentDataのエラーが消えます。

注意点 2:オプショナルチェーンを付ける

userAgentDataが JavaScript API で提供されていないブラウザで実行されるとエラーとなり意図しない挙動になるので、オプショナルチェーンをつけてundefinedを返すようにします。

const userAgentData = navigator?.userAgentData;

注意点 3:UAParser と併用して UA-CH で取得できないブラウザの判定をする

UA-CH で取得できないブラウザ(主に safari)の判定には、UAParser.js 等のライブラリを使用します。

ここでは UAParser を使用しましたが、正しく判定できるのであれば UserAgent や他のライブラリで問題ないです。

index.ts
(() => {
  if (navigator?.userAgentData){
    // UA-CHで取得できた際の処理
    console.log(navigator.userAgentData)
  } else {
    // UA-CHで取得できなかった為、UAParser.jsを利用する
    const browserInfo = uaParser.getBrowser() || '';
    // iPhoneだったら...など細かい処理
  }
})();

また、もし今後 safari が UA-CH に対応した時に取得できた際の処理をしてしまうのが困る場合は、Chrome または Edge のみ UA-CH を通して、他のブラウザは UAParser でブラウザ判定を行うなどの工夫が必要です。

index.ts
(() => {
  const CHROME = 'Google Chrome'; // UA-CHでのChrome
  const EDGE = 'Microsoft Edge'; // UA-CHでのedge
  const userAgentData = navigator?.userAgentData;

  if (userAgentData?.brands.length){
    // UA-CHで取得できた際の処理
    const browserInfo = userAgentData.brands.find(
       (data) => data.brand === CHROME || data.brand === EDGE
    );

     console.log(browserInfo)

     if(browserInfo.brand === CHROME){
	console.log("Chromeの場合の処理")
     }
  } else {
    // UA-CHで取得できなかった為、UAParser.jsを利用する
  }
})();

参考

https://developer.mozilla.org/en-US/docs/Web/API/Navigator/userAgentData

脚注
  1. tsconfig.json"lib": ["dom"]を指定する事で JavaScript の API やブラウザの提供する API の型定義を組み込むことができます ↩︎

Discussion