はじめに
うちの会社の年次目標にシステム作成、というか、ちょっとしたプログラムを作成すると宣言したので、せめて今まで使ったことのない言語を使ってみようということで、白羽の矢を立てたのがTypescript。
ちょうどJavascript系を触ってみたいなあと思っていたところなので、Typescriptを勉強がてら、この言語を使ってシステムを組んでみようか、という企て。
その前に
掲載するコードは以下のMWで動かしています。
- node 7.10以上
- Typescript 2.3.0以上
コードの実装や実行は Visual Studio Codeを使用しています。
クラスとインターフェース
Javascriptだとプロトタイプチェーンになるものの、Typescriptではインターフェース・抽象クラス・クラスの記述ができる
interface CallableBase {
constructName(): string;
}
abstract class Callable implements CallableBase{
abstract constructName(): string;
get FullName(): string {
return this.constructName();
}
}
class Person extends Callable {
firstName: string;
lastName: string;
id: number;
constructName(): string {
return this.firstName + " " + this.lastName;
}
}
class Place extends Callable {
countryName: string;
areaName: string;
constructName() {
return this.countryName + " " + this.areaName;
}
}
var p = new Person();
p.firstName = "京太郎";
p.lastName = "東";
var l = new Place();
l.areaName = "東京";
l.countryName = "日本";
var callList: Callable[] = [p,l];
callList.forEach(element => {
console.log(element.FullName);
});
型定義ファイルの定義
$(
$(document.body).ready(function() {
$("#YourMessage").text("Red");
$("#YourMessage").css("color","Red");
})
);
上記のtsスクリプトではjQueryを使用しています。
Typescript 1.X系だと、DefinitelyTypedやTypingsでjQueryなどの型定義ファイルを設定するのが一般的だったようです。
しかし、Typescript2.Xから、npmを使用した方式が利用可能で、すごく簡単でした。
# npm install @types/jquery
ただし、これで型解決する場合はあらかじめ以下の設定をする必要があります。
- npmの初期化
# npm init
- tsconfig.jsonでmoduleResolutionをnodeにする
{
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd' or 'es2015'. */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
/* Module Resolution Options */
"moduleResolution": "node" /* Specify module resolution strategy: 'node'
},
"exclude": [
"load_modules"
]
}
もっと詳しい説明はこちらにあります:TypeScript2.0時代の、型定義ファイル
トランスパイルするとコケる配列
こんなコードを書いてみました。
class Y {
private work: number[];
public get result(): number {
let sum:number = 0;
this.work.forEach(element => {
sum += element;
});
return sum;
}
public addValue(value: number): void {
this.work.push(value);
}
}
var y: Y = new Y();
y.addValue(11);
y.addValue(22);
y.addValue(33);
console.log("Y:::" + y.result);
このコード、エラー検出もなければごく普通にトランスパイルができるのですよ。
で、トランスパイル後のコードがこれ。
"use strict";
var Y = (function () {
function Y() {
}
Object.defineProperty(Y.prototype, "result", {
get: function () {
var sum = 0;
this.work.forEach(function (element) {
sum += element;
});
return sum;
},
enumerable: true,
configurable: true
});
Y.prototype.addValue = function (value) {
this.work.push(value);
};
return Y;
}());
var y = new Y();
y.addValue(11);
y.addValue(22);
y.addValue(33);
console.log("Y:::" + y.result);
いざ実行すると、
this.work.push(value);
でエラーになるのですよ。
TypeError: read property 'push' of undefined
つまり、work変数にpushなんて定義はないよ、と。
tsスクリプト上は問題なかったのにどうしてだろう、と型情報を見てみたら、
- any型になっている → any[]でないとおかしいのだけれど。
- よくよくトランスパイルされたコードを見ると、エラー発生のコードで初めてworkが使用されている
tsスクリプト上で宣言しかしていないからトランスパイルの時に消えたんだ!
そこで、workに対して明示的にArrayオブジェクトを突っ込んでみる。
class Y {
private work: number[] = new Array();
public get result(): number {
let sum:number = 0;
this.work.forEach(element => {
sum += element;
});
return sum;
}
public addValue(value: number): void {
this.work.push(value);
}
}
var y: Y = new Y();
y.addValue(11);
y.addValue(22);
y.addValue(33);
console.log("Y:::" + y.result);
でもってトランスパイル後のコード
"use strict";
var Y = (function () {
function Y() {
this.work = new Array();
}
Object.defineProperty(Y.prototype, "result", {
get: function () {
var sum = 0;
this.work.forEach(function (element) {
sum += element;
});
return sum;
},
enumerable: true,
configurable: true
});
Y.prototype.addValue = function (value) {
this.work.push(value);
};
return Y;
}());
var y = new Y();
y.addValue(11);
y.addValue(22);
y.addValue(33);
console.log("Y:::" + y.result);
実行してみると、
Y:::66
うまくでけた。
だから、コンストラクタなりなんなりで、配列は初期化したほうがよさそうな感じ。