LoginSignup
6
4

More than 5 years have passed since last update.

CoffeeScriptでも型が使いたい

Last updated at Posted at 2017-12-22

null安全では無い言語はプログラミング言語では無いとまで言われる昨今。null安全以前の動的型付け言語にとっては不遇の時代と言っても差し支えが無い。CoffeeScriptも変換先であるJavaScriptに引っ張られ動的型付け言語である。しかし、それは型情報ができない、型チェックができないことを意味しているわけでは無い。Pythonのmypyのように型アノテーションを追加したコードに別途型チェックができれば良いのだ。そう、それが、CoffeeScript v2からのFlow対応である。

準備

まずは必要なパッケージをインストールし、環境を整える必要がある。なおyarnを用いているが、npmでも同様のことは可能だ。

yarn add -D coffeescript
yarn add -D flow-bin

Flowの準備をする。

yarn run flow init
yarn run flow status

コマンドが正常に完了すればOKだ。

コード

早速コードを書いてみよう。

sample.coffee
# @flow

###::
type Person = {
  name: string,
  age: number,
};
###

showAge = (person ###: Person ###) ###: string ### ->
  "#{person.name}#{person.age}才です。"

taro ###: Person ### = {name: '太郎', age: 18}
console.log(showAge(taro))

# @flowがFlowのコードを表しており、必ず必要だ。###: ###/*: */に、###:: ###/*:: */に変換されるようになっている。これはComment Typesという書き方に準ずるようになっている。

実行とコンパイルとチェック

アノテーションはコメントであるためそのまま無視して実行する事も可能だ。

yarn run coffee sample.coffee

型チェックを行う場合は一旦JavaScriptに変換後に行う必要がある。

yarn run coffee --bare --no-header --compile sample.coffee
yarn run flow

No errors!と表示されていれば型が正しいと言うことになる。

JavaScriptに変換後は通常のFlowで書いた場合と同じである。違いはComment Typesを用いていることに過ぎない。Babelのflowプリセットを使えばコメント部分を削除することも可能だ。

エラーになるパターン

実際にエラーになる所を見ないと本当にチェックしているかわからない。そこで、エラーになるようなコードも試して見よう。

sample_error.coffee
# @flow

###::
type Person = {
  name: string,
  age: number,
};
###

showAge = (person ###: Person ###) ###: string ### ->
  "#{person.name}#{person.age}才です。"

taro ###: Person ### = {name: '太郎', age: 18}
hanako ###: Person ### = {name: '花子'}
console.log(showAge(taro))
console.log(showAge(hanako))
console.log(showAge("三四郎"))

花子と三四郎を新たに追加しようとしたが、花子は年齢不詳、三四郎にいたっては直接文字列を渡してしまっている。そのまま実行してもエラーにはならないが、無意味な出力結果を出す事になるだろう。そこで、Flowによるチェックを行うと、おかしな部分を確認できる。実行結果は下記のようになる。

Error: sample_error.js:19
              v
 19: hanako = {
 20:   name: '花子'
 21: };
     ^ object literal. This type is incompatible with
  8: var hanako/*: Person */, showAge, taro/*: Person */;
                   ^^^^^^ object type
  Property `age` is incompatible:
      8: var hanako/*: Person */, showAge, taro/*: Person */;
                       ^^^^^^ property `age`. Property not found in
                  v
     19: hanako = {
     20:   name: '花子'
     21: };
         ^ object literal

Error: sample_error.js:25
 25: console.log(showAge(hanako));
                         ^^^^^^ object literal. This type is incompatible with the expected param type of
 10: showAge = function(person/*: Person */)/*: string */ {
                                  ^^^^^^ object type
  Property `age` is incompatible:
     10: showAge = function(person/*: Person */)/*: string */ {
                                      ^^^^^^ property `age`. Property not found in
     25: console.log(showAge(hanako));
                             ^^^^^^ object literal

Error: sample_error.js:27
 27: console.log(showAge("三四郎"));
                         ^^^^^ string. This type is incompatible with the expected param type of
 10: showAge = function(person/*: Person */)/*: string */ {
                                  ^^^^^^ object type


Found 3 errors

最初のエラーはhanakoPerson型と一致しないことだ。そのため、hanakoPerson型ではないと判断され、次のエラーも発生する。最後は、Person型では無く文字列を渡していることがエラーである。このように、Flowを使えばどこがどのような問題なのかを表示してくれる。対象はJavaScript変換後のコードになるが、CoffeeScriptは対応付けがわかりやすいJavaScriptに変換してくれるので、それほど苦労せずに問題箇所を把握できるだろう。

まとめ

まずは導入とどんな感じであるかという紹介であるが、CoffeeScriptがさらに強力になるところがわかって貰えたと思う。ここらから、BabelでES5にトランスパイルしたり、webpackでまとめたりと一工夫必要になるのだが、そこら辺はおいおい手順を探っていきたいと思う。

こうして、型を手に入れたCoffeeScriptにもはや死角は無い。TypeScriptを打倒し、かつての栄光を取り戻す日も近い…かもしれない。

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4