昨年くらいからVueを使って(なんちゃって)フロントエンド開発をすることが多いんですが、フロントエンド界隈の話を聞いているとJavascriptではなくTypeScriptを使用するのが最近のトレンドのように感じます。
ということで、JavaScriptも未だにあんまりマスターできていないですが、TypeScriptも勉強してみたくなったので、今回はそのメモです。
今回参考にしたのはこちらの本です。
プログラミングTypeScript ―スケールするJavaScriptアプリケーション開発
- 作者:Boris Cherny
- 発売日: 2020/03/16
- メディア: 単行本(ソフトカバー)
TypeScript is なに?
サクッとTypeScriptとはなにかを語るには、公式サイトのこちらの文言で十分でしょう。
TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. TypeScript - JavaScript that scales.
要するに、誤解を恐れずいえば、"型のあるJavascript"です。 実際、裏では一度Javascriptに変換されて動作するので、TypeScriptでできることはJavaScriptでもできます。
じゃあ、なんで"型"なんてものをつけることがそんなに嬉しいのかというと、それはJavaScriptの動的型付けがあまりにいろんなことを裏でやりすぎて、テスト時(ひどいときには本番運用時)までバグに気づくことが難しいという問題があるためです。
JavaScriptの、多少おかしなコードであっても、勝手に型を解釈して動作させてしまう特徴は、品質確保という観点では非常に厄介な性質です。 できればコーディング時に適切なエラー出力により、コードを安全にしたいというニーズがあったようです。 そんなニーズに答えたのがTypeScriptであるというのが、ざっくりとしたTypeScriptの位置づけとなっています。
使ってみる
install
公式サイトに書かれている通りにインストールします。
npm install -g typescript
nodejsのインストールが必要なので、そちらもよしなにやっていただければと思います。
HelloWorld
なにはともあれはじめはHelloworldでしょうか。
helloworld.tsというファイルにコードを記述します。
console.log("hello world!")
それでは実行してみます。
TypeScriptは、実行する際には一度javascriptに変換されます。
$ tsc helloworld.ts
これで、helloworld.jsというJSのファイルが生成されます。 あとは、何も考えずにそれをnode.jsで実行してみます。
$ node helloworld.js hello world!
このへんまでがHello worldですね。 なんだかコンパイル言語をいじっているみたいな感覚になりますね。
TypeScriptプロジェクトを作る
TypeScriptでそれなりに大きなコードを作成しようとするとプロジェクトを作成する必要が出てきます。 TypeScriptでプロジェクトを作成する際にはこんな感じにするそうです。
- プロジェクトディレクトリ作成
- プロジェクト初期化
- npm init
- ライブラリインストール
- npm install --save-dev typescript tslint @types/node
- tsconfig.jsonの作成
- tsc --init
- tslint.jsonの作成
- ./node_modules/.bin/tslint --init
本の記載ではベタ書きするように書いてありますが、コマンドで作成したほうが安全な気はします。
sample_projectをコンパイル・実行してみます。
$ tsc $ node ./dist/index.js 3 6 { apple: 3, banana: 6 } 12
こんな感じで、設定ファイルをちゃんとしておくと*1distにjsファイルが作られたり、コンパイルコマンドがtscだけで済むようになったりするみたいですね。
型について
"Type"Scriptって言っているくらいだから、型について勉強しないことには始まりません。
基本形は大体上記の記事に書かれているものですべてかと思います。 詳しく勉強したい方は、書籍を買うことをおすすめします。
試しに型推論してみるとこんな感じで見えたりします。
$ node dist/index.js number string string object object object object object
コードの内容はgithubのコードをご参照ください。
関数
関数は関数で結構いろいろ考えて設計されているようでした。 ただ、あまり素晴らしさを実感できなかったので、なんか使うことになったらそのタイミングで再度復習したいと思います。
とりあえず、今回は関数の普通の書き方だけ覚えるようにします。
function greet1(name: string){ return "hello " + name } let greet2 = function(name: string){ return "hello " + name } let greet3 = (name: string) => { return "hello " + name } let greet4 = (name: string) => "hello " + name
引数を省略可能にする場合には"?"をつけて、引数の並びの末尾に持ってくるようにします。
let greet5 = function(name: string, family?:string){ return "hello " + name + " " + family } console.log(greet5("tom")) console.log(greet5("tom", "sam"))
実行するとこんな感じ。
$ tsc $ node dist/index.js hello tom undefined hello tom sam
その他、便利機能らしきものも多く紹介されていますが、それらについては使うことになってから復習することにします。
クラス・インタフェース
複雑なことをやろうとしたときにお世話になるのがクラスやインタフェースですね。
クラスはこんな感じに書きます。
class Dog { constructor( public name :string, public num : number, private bark: string ){} res(){ console.log(this.bark); } } let poti = new Dog("dog", 2, "bowwow") console.log(poti.name) console.log(poti.num) poti.res()
インスタンス化の際の引数はコンストラクタの引数として記述し、そこにアクセス修飾子を書くような形です。
動かすとこんな感じ。
$ tsc $ node dist/index.js dog 2 bowwow
パブリックな変数については、外部から呼び出すことができ、プライベートな変数についてもクラスメソッドを使用することでアクセスすることができました。
インタフェースはこんな感じですね。
interface Sushi { calories : number, salty : number, tasty: number } class magro implements Sushi{ calories = 3 salty = 4 tasty = 5 } let sushi = new magro() console.log(sushi.calories) console.log(sushi.salty) console.log(sushi.tasty)
インタフェースでは、コンストラクタなしで変数の型を宣言し、その実装はimplimentsキーワードを使用して記述していくようです。
$ tsc $ node dist/index.js 2 1 2
非同期プログラミング
JavaScriptの特徴として並行処理が挙げられます。 あまりJavaScriptに馴染みのない方だと結構混乱しますが、JavaScriptでは思わぬところで並行処理が実行されています。
なんとなく書いてみるとこんな感じです。
setTimeout(()=> console.log("A"), 1) setTimeout(()=> console.log("B"), 2) console.log("C")
このコードを実行すると、
$ node dist/index.js C A B
という感じに出力されます。 気が付かないうちに、並びと異なる感じに実行されていくわけです。
このような、感覚と異なる動作になっているものを、逐次実行の流れにするための方法はいくつかあって、例えばこんな感じにasync/awaitを使って書くことができます。
function PromiseA() { return new Promise(function(resolve) { setTimeout(function() {resolve("A")}, 1) }) } function PromiseB() { return new Promise(function(resolve) { setTimeout(function() {resolve("B")}, 2) }) } async function myAsync() { console.log(await PromiseA()) console.log(await PromiseB()) console.log("C") } myAsync();
これで実行すると、
$ node dist/index.js A B C
と順番通りになりました。
写経とかしてみたものの残骸
この辺においてあります。
こちらのコードは、Boris Cherny (著)『プログラミングTypeScript ―スケールするJavaScriptアプリケーション開発』(オライリー・ジャパン発行, ISBN-10: 4873119049)を参考にさせていただいております。
プログラミングTypeScript ―スケールするJavaScriptアプリケーション開発
- 作者:Boris Cherny
- 発売日: 2020/03/16
- メディア: 単行本(ソフトカバー)
感想
JavaScriptと似てるなーとか思いながら、一通り本を読んで勉強してみました。 多分、JavaScriptをちゃんと使っている人にとっては納得できるように作られている雰囲気は感じたのですが、いかんせんJavaScript自体の経験が私にあまりないので、どのへんが嬉しいのかはいまいちしっくり来なかったというのが本音です。 (特に型安全って言われても、、、って感じでした…) 何はともあれ一通り勉強したので、あとは習うより慣れろって感じで実際にものを作りながら慣れていきたいと思います。
あと、途中まで勉強したところで判明したんですが、どうやらTypeScriptはあまりVue.jsと相性が良くないようです。
厳密には、VueでTypeScriptは使えないわけではないようですが相性は良くない、ということのようです。 そういう観点で考えると、ケースバイケースではありますが、大規模で安定したフロントエンド開発を行う際にはReact + TypeScriptで行うのが良いというのが今の所の世論のようですね。
ということで、機会があったらReactも勉強して見たいと思いました。
*1:設定ファイルの内容はgithubのコードをご参照ください