Typescript感想 + Underscoreを扱えるようにしてみた

主に僕が一番使っているcoffee-scriptとの比較

書き味

構文の硬さがJavaが10 Python6 Rubyが3という記事をどっかで見たけど、同じように当てはめると、JS8 typescript6 coffee2という感じ。 coffee-scriptの超ゆるふわスタイルに慣れてると結構辛い感じはあるが、自分多分Coffeeについてある程度極まってる感じなので、あんまり比較しないほうがよさそう。 高階関数(後述)が簡単に定義できるのでJS特有のパラダイムには素直に応えられる。

””” multiline text “”” がない デフォルト引数がない

型アノテーション

クラス志向というよりかはインターフェース志向?Golangっぽさも若干感じる。とりあえずJSの邪魔をしないanyが便利。

JavaScriptのスーパーセット

CoffeeScriptの辛かった点である、「jsコピペしてすぐ動かない」問題が解決した。 とりあえずコピペしてから徐々に置き換えるという段階を踏める。

末尾セミコロン

JSと同じような扱い。classフィールドでは;なしだと構文解析されない。基本末尾セミコロン嫌いなので可能な限り省略して書いてみたが、結局必要そう…

Lambda

結局自分はfunctionと書きたくなくてcoffeescriptを使ってるフシがあるのだけど、どのぐらい省略できるか調べてみた。

自分自身を返す f(x) = x はこう書ける

1
(i) => {return i}

ブロックだと複数行書けるが、returnが必須

もしくはブロック省略して

1
(i) => i

こちらはreturnが要らない

CoffeeScriptと違って 引数なしの()は省略できない

1
2
3
[1,2,3].forEach((i) => {
  console.log(i);
})

補完

宗教上の理由(一方が好きというかは一方が嫌い)でVSが使えないのだが、SublimeTextで書いていたが補完はなかった。 まあ簡単そうなのでそのうち誰かが作るだろう。

instance

公式リポジトリのサンプルに型ファイル情報が少しついてる

  • express.d.ts
  • jquery.d.ts
  • jqueryui.d.ts
  • node.d.ts

todomvcにはBackboneモジュールに対して型アノテーションを追加していて参考になる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
declare module Backbone {
    export class Model {
        constructor (attr? , opts? );
        get(name: string): any;
        set(name: string, val: any): void;
        set(obj: any): void;
        save(attr? , opts? ): void;
        destroy(): void;
        bind(ev: string, f: Function, ctx?: any): void;
        toJSON(): any;
    }
    export class Collection {
        constructor (models? , opts? );
        bind(ev: string, f: Function, ctx?: any): void;
        collection: Model;
        length: number;
        create(attrs, opts? ): Collection;
        each(f: (elem: any) => void ): void;
        fetch(opts?: any): void;
        last(): any;
        last(n: number): any[];
        filter(f: (elem: any) => any): Collection;
        without(...values: any[]): Collection;
    }
    export class View {
        constructor (options? );
        $(selector: string): any;
        el: HTMLElement;
        $el: any;
        model: Model;
        remove(): void;
        delegateEvents: any;
        make(tagName: string, attrs? , opts? ): View;
        setElement(element: HTMLElement, delegate?: bool): void;
        tagName: string;
        events: any;

        static extend: any;
    }
}

underscoreに型アノテーションつけてみた

練習がてらやってみた。 underscore type definition — Gist https://gist.github.com/3831467

クライアント

1
2
///<reference path='underscore.d.ts'/>
declare var _ :underscore;

サーバー(Node)

1
2
3
///<reference path='node.d.ts'/>
///<reference path='underscore.d.ts'/>
var _ : underscore = require('./underscore-min')

ドキュメントみながら脳みそ止めて適当に作ったのでテストはしてないがひと通り動くはず。

underscore.d.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
interface underscore {
  //collections
  each(obj:any, f: Function): void;
  each(obj:any, f: Function, context:any): void;
  forEach(obj:any, f: Function): void;
  forEach(obj:any, f: Function, context:any): void;

  map(obj:any, f: Function): any[];
  map(obj:any, f: Function, context:any): any[];
  collect(obj:any, f: Function): any[];
  collect(obj:any, f: Function, context:any): any[];

  reduce(obj:any, f: Function, memo:any): any;
  reduce(obj:any, f: Function, memo:any, context:any): any;
  inject(obj:any, f: Function, memo:any): any;
  inject(obj:any, f: Function, memo:any, context:any): any;
  foldl(obj:any, f: Function, memo:any): any;
  foldl(obj:any, f: Function, memo:any, context:any): any;

  reduceRight(obj:any, f: Function, memo:any): any;
  reduceRight(obj:any, f: Function, memo:any, context:any): any;
  foldr(obj:any, f: Function, memo:any): any;
  foldr(obj:any, f: Function, memo:any, context:any): any;

  find(obj:any, f: Function): any;
  find(obj:any, f: Function, context:any): any;
  detect(obj:any, f: Function): any;
  detect(obj:any, f: Function, context:any): any;

  reject(obj:any, f: Function): any[];
  reject(obj:any, f: Function, context:any): any[];

  filter(obj:any, f: Function): any[];
  filter(obj:any, f: Function, context:any): any[];
  select(obj:any, f: Function): any[];
  select(obj:any, f: Function, context:any): any[];

  all(obj:any, f: Function): bool;
  all(obj:any, f: Function, context:any): bool;
  every(obj:any, f: Function): bool;
  every(obj:any, f: Function, context:any): bool;

  any(obj:any): bool;
  any(obj:any, f: Function): bool;
  any(obj:any, f: Function, context:any): bool;
  some(obj:any): bool;
  some(obj:any, f: Function): bool;
  some(obj:any, f: Function, context:any): bool;

  include(obj:any, f: Function): bool;
  contains(obj:any, f: Function): bool;

  invoke(obj:any, f: Function): any[];
  invoke(obj:any, f: Function, args:any[]): any[];

  pluck(obj:any, prop: string): any[];

  max(obj:number[]): number;
  max(obj:number[], context:any): number;

  min(obj:number[]): number;
  min(obj:number[], context:any): number;

  sortBy(obj:any, f: Function): any[];
  sortBy(obj:any, f: Function, context:any): any[];

  groupBy(obj:any, f: Function): any[];

  sortedIndex(obj:any, f: Function): any[];
  sortedIndex(obj:any, f: Function, iter:Function): any[];

  shuffle(obj:any[]): any[];
  toArray(obj:any[]): any[];
  size(obj:any[]): number;

  //array
  first(obj: any[]): any;
  head(obj: any[]): any;

  last(obj: any[]): any;

  rest(obj: any[]): any[];
  tail(obj: any[]): any[];

  compact(obj:any[]): any[];

  flatten(obj:any[]): any[];
  flatten(obj:any[], shallow:number): any[];

  without(obj:any[]): any[];
  intersection(...obj:any[]): any[];
  difference(...obj:any[]): any[];

  uniq(obj:any[]): any[];
  unique(obj:any[]): any[];

  zip(...obj:any[]): any[];
  indexOf(obj:any[], val:any): any;
  range(stop:number): number[];
  range(start:number, stop:number): number[];
  range(start:number, stop:number, step:number): number[];

  //object
  keys(obj:any): string[];
  values(obj:any): any[];
  functions(obj:any): string[];
  extend(obj:any, ...sources:any[]): any;
  pick(obj:any, ...keys:string[]): any;
  defaults(obj:any, ...defaults:any[]): any;
  clone(obj:any): any;
  tap(obj:any, intercepter:Function): any;
  has(obj:any, key:string): bool;

  //functions
  bind(f:Function, obj:Object):void;
  bind(f:Function, obj:Object, ...args:string[]):void;
  bindAll(obj:Object, ...methodNames:string[]):void;
  memoize(f:Function):any;
  memoize(f:Function, ...hashFunctions:any[]):any;

  delay(f:Function, wait:number):any;
  delay(f:Function, wait:number, ...arguments:any[]):any;
  defer(f:Function):any;
  defer(f:Function, ...arguments:any[]):any;
  throttle(f:Function, wait:number):any;
  debounce(f:Function, wait:number):any;
  debounce(f:Function, wait:number, ...immediate:any[]):any;
  once(f:Function):any;
  after(count:number, f:Function):any;
  wrap(f:Function, wrapper:Function):any;
  compose(...fs:Function[]):Function;

  //isX
  isEqual(obj:any, other:any): bool;
  isEmpty(obj:any): bool;
  isElement(obj:any): bool;
  isArray(obj:any): bool;
  isObject(obj:any): bool;
  isArguments(obj:any): bool;
  isFunction(obj:any): bool;
  isString(obj:any): bool;
  isNumber(obj:any): bool;
  isFinite(obj:any): bool;
  isBoolean(obj:any): bool;
  isDate(obj:any): bool;
  isRegExp(obj:any): bool;
  isNaN(obj:any): bool;
  isNull(obj:any): bool;
  isUndefined(obj:any): bool;

  //utility
  noConflict():bool;
  identity(x:any):any;
  times(n:number, f:Function):void;
  mixin(obj:any):void;
  uniqueId(prefix:string[]): string;
  escape(str:string): string;
  result(obj:any, key:string): any;
  template(template:string, bindings:any): string;

  //chaining
  chain(obj:any):any;
  //value is useless
}

追記: カリー化したかった

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// it doesnt work
Function.prototype.proc = function() {
  var args, target, v;
  var __slice = [].slice;
  args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
  target = this;
  while (v = args.shift()) {
    target = target(v);
  }
  return target;
};

var fuga: (number) => (string) => number
  = (a) => (b) => a * b.length;

// it works
console.log(fuga(3)("dafa"))

// it doesnt work
console.log(fuga.proc(3, "dafa"))

var fuga: (number) => (string) => number

なんていうかっこいい型定義ができるんだからcurry化したいなーと思っていじってたんだけど、Function型定義がprocを認識しない。 どうにかしてdeclareすればできるのかと思ったんだけどよくわからない。誰かタスケテほしい。

こういうことをしたかったんだけど