TypeScriptで外部moduleをexportするときのまとめ
こんにちは、@armorik83です。今年もよろしくお願いします。
今回はTypeScript (with commonjs)のexport周りについて、あまりにも何度も同じ過ちを犯しているためメモを残します。
TypeScriptでのexportの書き方は多い?
TypeScriptで外部モジュールをexportさせるとき、書きうるスタイルが多いイメージを勝手に持っていましたが、実際には一通りしかありません。
その前にJavaScriptのexportの方法をおさらい
多いイメージを持っていた原因ですが、まずJavaScriptには用途別の2つのexport方法があります。(追記: 勝手に2つあるように思い込んでいただけでした)
exports.foo = foo;
foo
は関数です。ソース内に複数のパブリックな関数がある場合に、
function foo() {
// ...
}
function bar() {
// ...
}
exports.foo = foo;
exports.bar = bar;
というように書きます。使うときは、
var mod = require('./mod');
mod.foo();
mod.bar();
// mod() これはできない
mod
変数内のプロパティfoo
, bar
に関数が入ってます。
module.exports = foo;
こちらはソース内に公開する関数が単一の場合に用います。Gruntfile書いてたらおなじみですね。
module.exports = function(grunt) {
// ...
}
もしmod.js
でこのような書き方をした場合、使う側では、
var mod = require('./mod');
mod();
いきなり変数名mod
に()
を付けて使えるわけです。
TypeScriptでのこれらの書き方
TypeScriptでの書き方は上記JavaScriptでの書き方と一緒ではありません。しかし、一緒の書き方でも動いてしまいます。このJavaScriptの書き方でも動いてしまうところが、TypeScriptってexportの書き方多いよな…と変なイメージを持つ理由でした。
正しくは次のようになります。
出力結果をexports.fooにしたい
export function foo() {
// ...
}
export function bar() {
// ...
}
classの場合export class ClassName
です。
出力結果をmodule.exportsにしたい
function foo() {
// ...
}
export = foo;
export =
と書きます。classの場合はexport = ClassName;
です。
ダメな例
function foo() {
// ...
}
exports = foo;
これ最悪。exports
は宣言済み変数名なので、エラーは出んわ動かんわと一番やってはいけない例。こんなの書いてたら恥ずかしいよ!(わたしです)
なぜTypeScriptの書き方をすべきか
前に書いた通りJavaScriptの書き方でも動いてしまいますが、TS上でJSの書き方をするとimport mod = require('mod');
で型情報を引っ張って来られません。TS上でJSの書き方をすると、大半のケースで型エラーを起こすはずです。
本質
(投稿後追記)…と上のようなぬるい記事をのたまっていたところ、毎度お世話になりっぱなしの@vvakame先生から「仕様書とか実装読んだほうが早いし正確です」と、ごもっともすぎる意見を頂いたのでDocを読みました。
- http://wiki.commonjs.org/wiki/Modules
- http://nodejs.jp/nodejs.org_ja/docs/v0.10/api/modules.html#modules_module_exports
モジュール内で利用出来る exports 変数は、最初は module.exports への参照です。
ガイドラインとして、もし exports と module.exports の間の関係が魔法のように 見えるなら、exports を無視して module.exports だけを使うようにしてください。
ということです。前提知識が間違ってます。
昨今はBrowserifyやwebpackといったブラウザでexports, requireする方式が主流。いいかげんさっさとTypeScriptのexportを叩き込んでおくのが良さそうです。
それではまた!
Discussion