Edited at

TypeScriptプロジェクトに独自の型定義を配置する方法


概要

TypeScriptのアプリケーションにとって、NPMライブラリを使う際に型定義ファイルが用意されてないライブラリは厄介なものです。

無理やり require("...") でJSのまま読み込ませてもいいですが、ちゃんとした型定義を用意したくなるときもあります。

作成した型定義ファイルは元のNPMライブラリに取り込んでもらい package.jsontypes で指定してもらう方法 1DefinitelyTyped に取り込んでもらい @types/*** をnpm installする方法がありますが、どちらも取り込んでもらうためにはPull Requestを投げるなりで時間がかかってしまいます。

Pull Requestは投げつつ、それが取り込まれてリリースされるまでの間、自分のプロジェクトで作った型定義ファイルを利用できる方法はないものかと思って調べてたんですが、なかなかちょっと面倒くさかったのでここにまとめておこうと思います。


実行環境


  • Node.js 10.5.0

  • TypeScript 2.9.2


やってみる

今回はDefinitelyTypedのREADMEでもいい型定義ファイルとしてなぜか推奨されている base64-js を利用します。 2


For a good example package, see base64-js.

https://github.com/DefinitelyTyped/DefinitelyTyped#create-a-new-package



base64-js の型定義

base64-jsは公開されている関数が3つしかないため、型定義ファイル も次のようなシンプルなものです。

// Type definitions for base64-js 1.2

// Project: https://github.com/beatgammit/base64-js
// Definitions by: Peter Safranek <https://github.com/pe8ter>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped

export function byteLength(encoded: string): number;
export function toByteArray(encoded: string): Uint8Array;
export function fromByteArray(bytes: Uint8Array): string;

今回はこのライブラリを利用した簡単なソースコードで実験を行います。


index.ts

import { toByteArray } from "base64-js";

import { TextDecoder } from "util";

const encoded = "44GT44KT44Gr44Gh44Gv"; // "こんにちは" をBase64エンコードしたもの
console.log(`encoded: ${encoded}`);
const decoded = new TextDecoder().decode(toByteArray(encoded));
console.log(`decoded: ${decoded}`);



設定方法

今回は独自の型定義ファイルを配置する方法を確認するため、NPMの @types/base64-js は利用せず、この型定義をプロジェクト内に配置することにします。

structure.png

/types/base64-js/index.d.ts に型定義を配置しましたが、このままではコンパイルすることはできません。

tsc が型定義ファイルの位置を知らないためです。

$ npm run build


> original-definition-in-typescript-project-sample@1.0.0 build /Users/mtgto/work/js/original-definition-in-typescript-project-sample
> tsc --project tsconfig.json

src/index.ts:1:29 - error TS7016: Could not find a declaration file for module 'base64-js'. '/Users/mtgto/work/js/original-definition-in-typescript-project-sample/node_modules/base64-js/index.js' implicitly has an 'any' type.
Try `npm install @types/base64-js` if it exists or add a new declaration (.d.ts) file containing `declare module 'base64-js';`

1 import { toByteArray } from "base64-js";
~~~~~~~~~~~

これをコンパイラに教えてあげるため、 tsconfig.jsontypeRoots, baseUrl, paths を次のように変更します( typeRoots のデフォルトは [] です)。

{

"compilerOptions": {
"baseUrl": "./",
"paths": {"base64-js": ["types/base64-js"]},
"typeRoots": ["types", "node_modules/@types"],
}
}

paths によって import * as base64 from "base64-js" のように相対パスではなく書くことが許されます。

baseUrlpaths を使う際に設定する必要があります。ソース内で相対パスではなくimportしたいときなんかは src を指定したりすることがあるようです。 3

また paths"paths": {"*": ["types/*"]}, のように書くこともできます。

typeRoots 4 は実は今回のケースでは書かなくてもコンパイルできます。すでに typeRoots を指定しているときに独自型定義フォルダを追加する必要があります。

すでに型定義ファイルをもっているライブラリを上書きする形でしたいときに独自型定義を使いたいときに設定するのかなとおもって @types/base64-js をインストールした上で typeRoots から types ディレクトリを消してみたりしたのですが、ちゃんと types/base64-js/index.d.ts にしかない関数を呼んだコードをコンパイルすることができました。

各項目について詳しくはtsconfig.jsonの説明 も参考にしてください。


実行してみる

無事に独自型定義でコンパイルが通ったので実行してみます。

$ node .

encoded: 44GT44KT44Gr44Gh44Gv
decoded: こんにちは

動きました。以上です。


補足

試行錯誤してたときにだめだったことを書いておきます。


よくない例: 独自型定義ファイルを *.ts から相対パスで参照する

コンパイルは通りますが、JSが参照するライブラリが相対パス表記になってしまい、実行時に「ライブラリが見つからない」とnodeに怒られました。

コンパイル後に .js ファイル内の相対パスを書き換える強引な手もありますが、SourceMapとの関連がずれることもあるでしょうし、やはりおすすめできません。


おすすめしない例: 独自型定義ファイルを node_modules/@types に配置する

.gitignore で node_modules 以下を管理していることは多いのでおすすめできません。


サンプルコード

今回の記事で紹介したコードを以下で確認できます。

https://github.com/mtgto/original-definition-in-typescript-project-sample


まとめ


  • TypeScriptの独自型定義を置くときには tsconfig.jsonbaseUrl, paths に絶対パスで指定できるように設定をしましょう



    • typeRoots を使っているときは typeRoots にも追加しましょう



  • もしよければライブラリかDefinitelyTypedにPRを送り、他の人に貢献しましょう!





  1. https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html#including-declarations-in-your-npm-package 



  2. 型定義を新たに作ってみようと思うときに参考にしようとしてもbase64-jsはシンプルすぎて役に立たないことのほうが多いと思います 



  3. 例えば https://stackoverflow.com/a/43330003 



  4. https://www.typescriptlang.org/docs/handbook/tsconfig-json.html#types-typeroots-and-types