Mapboxの非表示シンボルを扱う (3. 型の拡張編)

2022-09-24

thumbnail

Mapbox GL JSの画面上で別のシンボルに隠されたシンボルを扱うユーティリティライブラリを開発中です。 これはライブラリの開発過程を紹介するシリーズのブログ投稿第3弾です。

背景

本シリーズの過去の2回の投稿で以下を示しました。

このブログ投稿では、Mapbox GL JS (mapbox-gl-js)のいくつかの型がTypeScriptで利用できないという残課題に挑みます。

ライブラリ(mapbox-collision-boxes)は私のGitHubレポジトリで手に入ります。

TypeScript?

TypeScriptはJavaScriptの亜種で、強力な型機能を提供します。 詳しくはTypeScriptの公式ウェブサイトを参照ください。 このページ[1]はTypeScriptとJavaScriptの違いを理解するのに役立ちます。 mapbox-collision-boxesの実装言語にはTypeScriptを選択しました。

mapbox-gl-jsには型が定義されているのか?

答えは「はい」ですが、mapbox-gl-jsはJavaScriptに型を導入する別の流儀であるFlowで型を定義しています。 残念ながら、Flowの型定義はTypeScriptと互換性がありません。 なのでmapbox-gl-jsの外側にTypeScriptの型定義が必要です。 TypeScriptコミュニティのコントリビュータさんが頑張ってくださっているおかげで、これらの型は@types/mapbox-gl*として利用することができます。

* このブログを書いているときの@types/mapbox-glの最新バージョンは2.7.5で、これはmapbox-gl-jsバージョン2.7をベースにしています。 当時のmapbox-gl-jsの最新版は2.10ですが、バージョンの違いによる問題にはとりあえず遭遇していません。

@types/mapbox-glが欠いているもの

@types/mapbox-glmapbox-collision-boxesが依存しているいくつかのプロパティや型を公開していません。 たとえば、Mapクラス(@types/mapbox-gl/index.d.ts#L201-L602)はstyle: Styleプロパティを欠いています。 @types/mapbox-glBucketクラスもSymbolBucketも定義していません。

どう対処するか?

型チェックを諦めるというのに惹かれるかもしれませんが、これは最終手段です。

mapbox-gl-jsの既存の型定義を拡張するにはモジュール拡張(Module Augmentation)を使うことができます。 「モジュール拡張」はTypeScriptの少し踏み込んだ機能です。 詳しくはTypeScriptのドキュメントの参照ください。

たとえば、以下のスニペットはMapクラスにstyle: Styleプロパティを追加します。

import { Map, Style } from 'mapbox-gl';

declare module 'mapbox-gl' {
    interface Map {
        style: Style;
    }
}

上記の宣言の後は、Map#styleに型安全にアクセスできます。

BucketSymbolBucketについては、単純に@types/mapbox-glに存在していないのでインターフェイスを追加するだけで良いです。

export interface Bucket {}

export interface SymbolBucket extends Bucket {
  bucketInstanceId: number;
  // ... 他のプロパティ
}

指定したBucketSymbolBucketかどうかをチェックするユーティリティ関数(mapbox-collision-boxes/private/mapbox-types.ts#L232-L234)も導入しました。これはTypeScriptの「型述語(Type Predicates)」という機能を利用しています。

export function isSymbolBucket(bucket: Bucket): bucket is SymbolBucket {
  return (bucket as SymbolBucket).symbolInstances !== undefined;
}

mapbox-collision-boxesを実装するのに最低限必要な定義は私のGitHubレポジトリにあります。

まとめ

今回の短い投稿では、以下のTypeScriptの機能を紹介しつつmapbox-gl-jsの既存の型(@types/mapbox-gl)をどうやって拡張するかを示しました。

mapbox-collision-boxes私のGitHubレポジトリで手に入ります。

参考

  1. TypeScript for the New Programmer - https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html (TypeScriptとJavaScriptの違いを理解する)