Vue.js + TypeScript でUIkitを使う

 皆さんお疲れさまです。最近、寒かったり熱かったりで気まぐれな気候ですが、いかがお過ごしでしょうか。
最近の話なんですが、チキンラーメンを作ろうと思い立ち、器に麺を入れてお湯を入れたんですが、ちょびっと出ただけでお湯が尽きてしまいました。
このままお湯が沸くまで待っていたら、ふやけて美味しくなくなってしまうので、これはもう焼きそばにするしかないと、フライパンに入れて炒めたんですがね。
これが意外とイケるんですね。個人的にポン酢をかけるといい感じに味がしまって美味しかったですね。
チキンラーメンとポン酢。意外とありですよ。

あと、最近会社近くの立ち食い蕎麦屋で、ざるそばを注文したんですがね。温かいぶっかけ蕎麦が出てきました。店員さんに「あれ、ざるそば頼んだんですが……」って言ったら、
「ざるそばは夏にしかないよ笑笑」って笑われました。美味しかったですけどね。ぶっかけ蕎麦も。

Vue.js + UIkit を使っていた

 やっと本題です。なんとか蕎麦エピソードを冒頭に入れたくてしょうがないのです。
この前、Vue.js と UIkit を使ってかんたんなWebアプリケーションを作成していました。
そんな折り、

「TypeScript 使えば VSCode君のインテリセンスフル活用できるぜ」

なんて情報をちらほらみかけたので、

「なら、自分も TypeScript に乗り換えてみるか……そんなでかいコードじゃないし、楽やろ」

なんて軽い気持ちで TypeScript を導入しました……
たしかに、ロジックや宣言自体は小さな変更で済んだのですが、各モジュールをimportする

import UIkit from "uikit";
import Icons from "uikit/dist/js/uikit-icons";

UIkit.use(Icons);

のところで、

Could not find a declaration file for module 'uikit/dist/js/uikit-icons'

なんてエラーが出て、UIkit のモジュールをインポートすることができません。

$ yarn add types/uikit

とかも試しましたが、上記エラーは解決されず……
どうやら、JavaScript で書かれた UIkit の uikit-icons というモジュールを TypeScript で使用するのに必要な
「型定義ファイル」がUIkit側では用意されていないことが原因のようでした。

型定義ファイルを作ろう

そうと分かれば、型定義ファイルを作成するか……となるのですが、uikit-icons.js のコードを読みに行ってみると

(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define('uikiticons', factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.UIkitIcons = factory());
}(this, (function () { 'use strict';
    function plugin(UIkit) {
        // いろんな処理……
    }
    // いろんな処理……
    return plugin;
})));

パット見、何がexportされてるのか分かりづらくないですか……?
JavaScriptに不慣れな私にはなんだこれ???ってなってたのですが、よく見てみるとexportされるオブジェクトの型を限定できる書き方なのかなと納得しました。
なんだか初めてSFINAEを見たときと同じ感覚がして、懐かしく思いました。

とりあえず、exportされるのは object 型ということがわかったので、これを元に型定義ファイルを作成していきます。

export const Icons: object;

こんだけです。セオリー通りの書き方かどうかはわかりませんが、とりあえず動くのが優先です。
さて、ここからがハマった所なんですが、uikit/dist/js/uikit-icons に対応する型定義ファイルを tsconfig.json の path に追記してあげないといけません。
今回は対応する型定義ファイルを (root)/src/types 内においているため、設定は以下のようになります。

"compilerOptions": {
    "paths": {
      "uikit/dist/js/uikit-icons": [
        "src/types/uikit-icons"
      ]
    }
  }

import するソース側は

import UIkit from "uikit";
import Icons from "uikit/dist/js/uikit-icons";

(UIkit.use as (UIkit: object) => void)(Icons);

としてあげることで、正常に UIkit のモジュールを読み込むことができます。

独り言

色々検索してみたんですけど、特に Vue.js + TypeScript + UIkit の構成でこの問題に対処している記事がなかったので、書いてみました。
UIkit の issue に似たようなことがいくつか書いてあったんですけど、 TypeScript 初心者の私には省かれた常識を察することができませんでした……
もっと精進せねば。