typescriptで書いたコード中で雑に外部のjsを読み込んでnode.js上で実行する方法(typescriptで書いたangular.jsのコードをnode.js上で手軽に実行する方法)について

きっとtypescriptなどに慣れている人にはごく当たり前のことなのだと思うのだけれど。自分用のメモも兼ねてまとめる。

はじめに

typescriptで書いたコードの動作を確認したいものの実行時には少しだけjs側のコードによる準備が必要な場合がある。 (例えば、typescriptで書いたangular.jsのコードをjsdom上で動作させるためにbenvを使うなど) そのような場合に必要な関数の全てに対して型をつけるのが面倒くさいという時にどうするかという話。

方法

方法は以下の通り

  • 外部jsは普通にcommonjsのスタイルで書く
  • requireに型をつける。型定義を省略したい外部jsに関しては、es6形式のmoduleではなくrequireを使ってimportする

このようにするとtscの型検査の範囲では、requireの部分までしか読み込まれないので楽。

typescriptで書いたangular.jsのコードをnode.jsで動かせるようにする。

app.ts

/// <reference path="./angular.d.ts" />

interface MySetupFunction {
  (callback: (angular: ng.IAngularStatic) => void): void;
}

declare var require: (module: string) => any;
const setup: MySetupFunction = require("./setup");

setup((angular: ng.IAngularStatic) => {

  // do something
});

外部のjsとしてsetup.jsを別途作っているが。typescriptが関知する部分はsetup.jsでexportされている関数だけ。 setup.jsのAPIとしてsetup.js自体が依存しているライブラリに関する値の型などは考慮する必要がない。 もちろん、angualr.d.tsなどの型定義ファイルは必要。

setup.jsは以前にも紹介したbenvを使ったangular.jsをnode.js上で動かせるようにした関数のこと。

setup.js

'use strict';
var benv = require('benv');

function setup(cb){
  benv.setup(function(){
    global.Node = window.Node;
    benv.expose({
      angular: benv.require(require.resolve("angular/angular"), "angular")
    });
    cb(window.angular);
  });
}

module.exports = setup;

特に特別なことをしていないので、以下で動く

$ tsc app.ts // この時setup.jsの内部はtscにより解釈されない
$ node app.js // この時setup.jsの内部はnodeにより解釈される

apendinx: angular.d.tsについて

gistに投稿するということを念頭に入れるとファイル構造自体はフラットな形の方が嬉しい。 DefinitelyTypedなどから取ってきたangualr.d.tsはjquery.d.tsを必要とするが、このpathの指定がフラットな形式ではなく以下のような形で置かれることを想定しているらしい。

.
├── angular
│   └── angular.d.ts
└── jquery
    └── jquery.d.ts

このためsed(macではgsed)で以下の様にして変えてしまうと良い。

$ wget https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/master/jquery/jquery.d.ts
$ wget https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/master/angularjs/angular.d.ts
$ sed -i 's@../jquery/@./@' angular.d.ts

gist

動くサンプルは以下に

gistdcd6b25a2d1f0806e96a