著者のバックグラウンド
他の言語経験者でJavascriptは知っている程度です
- 業務で使う言語: Python
- API程度なら作ったことある: Go
- 学生時代: C, Perl
- 触ったことある: C++, JavaScript
書籍
下記の書籍でどの程度まで理解できるか試してみました。
速習TypeScript
https://www.amazon.co.jp/dp/B0733113NK
TypeScriptの利点
- 開発環境が豊富
- JavaScriptのスーパーセット
- Google, Microsoftで標準採用
- ECMAScript2016に対応
環境構築について
簡易的な手法
下記の環境でブラウザ上で動作させることができます。
https://www.typescriptlang.org/play/
コマンドラインでの環境構築
コマンドラインで実行できると実際の開発の場面で役に立つのでその手法も記述されていました。
node.js の導入
下記で環境ごとに導入が可能です。
https://nodejs.org/en/download/package-manager/
TypeScriptの導入
npm install -g typescript
TypeScriptの文法
型推論
データ型を指定せずに入力もできます。ただしデータ型は型推論によって数値型が推論されているので下記の場合でもエラーがでます。
任意の型
anyは特別な場合を覗いて使用しないで下さい。型を設定しているTypeScriptの良さを活かせないからです。
リテラル
特徴的なテンプレート文字列について紹介されていました。
let mail: string = 'admin@example.com';
let msg = `レビューアー募集中!
書籍の感想下さい。 ${mail}`;
改行コードが不要な点と宣言済みの変数を簡単に埋め込むことができます。
型アサーション
型キャストして型を明示的に変更できます。型キャストは型チェクの機能が働きづらくなる原因にもなるので使う場所は最小限に抑える方がベターです。書籍には具体例も載っていました。
ブロックスコープに対応したlet命令
var
宣言の場合はブロック外でも1が表示されます。let
命令で宣言するとブロック外は無効になります。また同一スコープ内の重複した変数宣言は許可しません。
let
を使用するとスコープ最小化、重複チェック自動化されるためlet
を変数宣言に使用する方がベターです。
const命令
後から再代入することを防いでいます。ただし配列などの各要素の置き換えができます。書籍には具体例も載っていました。
他の言語にもある一般的なもの
- 配列・連想配列・列挙型・タプル
- 多次元配列
- 配列生成方法
- 連想配列
- 列挙型
複数の型が混在したタプル型
複数の型が存在しているため下記のような処理の場合にコンパイルが通るがエラーが出る事象があるため積極的に使用しない方が良い
共用型
複数の型のいずれかを表す
型エイリアス
特定の型に対して別名(エイリアス)を設定する仕組みです。
下記の理由からインターフェースでできる場合は使用しない方がベターです。
- エラーメッセージに別名が表示されない
- 継承・実装では利用できない
null非許容型
compileOptions
のstrictNullChecks
オプションにtrueを設定することですべての型でnull/undefinedを禁止出来ます。
型定義ファイルについて
書籍ではJavaScriptのライブラリを使用する際に必要な型定義ファイルについても言及されていました。
これによってTypeScriptでもJavaScriptのライブラリが使用可能になるようです。
型定義ファイルの詳細
https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html
関数
他の言語にもある一般的なもの
- function 命令
関数リテラル
let triangle = function(base: number, height:number): number {
return base * height / 2;
}
console.log(triangle(10, 5));
アロー関数
let triangle = (base: number, height: number): number => {
return base * height / 2;
};
console.log(triangle(10, 5));
オーバーロード
最初にシグネチャだけ用意して後で関数の詳細を実装する方法。シグネチャ部分で型チェックをする
引数・戻り値型としての共用型
function square(value: number): number | boolean {
if (value < 0){
return false;
} else {
return Math.sqrt(value);
}
}
console.log(square(9));
console.log(square(-9));
型ガード
string型のみ通すようにチェックする。numberの型だと通らないようにする。
クラス
クラス命令
class Person {
private name: string;
private sex: string;
constructor(name: string, sex: string) {
this.name = name;
this.sex = sex;
}
public show(): string {
return `${this.name} ${this.sex}`;
}
}
let p = new Person('Rie', 'Female');
console.log(p.show())
静的メンバー
class Figure {
public static PI: number = 3.14159;
public static circle(radius: number): number {
return radius * radius * this.Pi;
}
}
console.log(Figure.Pi);
console.log(Figure.circle(5));
名前空間
exportを付けないとアクセス許可されないのでつけます。
階層的な名前空間
namespace Wings.MainApp {
export class Hoge {}
export function foo {}
}
let h = new Wings.MainApp.Hoge();
Wings.MainApp.foo();
継承
class Person {
private name: string;
private sex: string;
constructor(name: string, sex: string) {
this.name = name;
this.sex = sex;
}
public show(): string {
return `${this.name} ${this.sex}`;
}
}
class BusinessPerson extends Person {
work(): string {
reutrn `${this.name}`;
}
}
let p = new BusinessPerson('Rie', 'female');
console.log(p.show());
console.log(p.work());
オーバーライド
superの部分で規程クラスの要素やメソッドを呼び出している。
抽象メソッド
abstract class Figure {
constructor(protected width: number, protected height:number) {}
abstract getArea(): number;
}
class Triangle extends Figure {
getArea(): number {
return this.width * this.height / 2;
}
}
let t = new Triangle(10, 5);
console.log(t.getArea());
インターフェース
書籍では抽象クラスと比較してインターフェースの必要性を述べていました。
interface Figure {
getArea(); number;
}
class Triangle implements Figure {
constructor(private width: number, protected height: number) {}
getArea(): number {
return this.width * this.height / 2;
}
}
let t = new Triangle(10, 5);
console.log(t.getArea());
構造的部分型
インターフェースを継承していなくてもインターフェースのメソッドをすべて備えている場合にオブジェクトの代入が可能
型注釈としてのインターフェース
interface Car {
type: string;
run(): void;
}
let c: Car = {
type: 'トラック',
run() {
console.log(`${this.type} is run`);
}
};
c.run();
オブジェクト型のリテラル
その場限りの型情報の明示
let c1: {type: string, width: number} = {type: '軽トラック', weight: 750};
コールシグネチャ
interface Car2 {
(type: string) : string;
}
let c2: Car2 = function(type: string): string {
return `Car type is ${this}`;
}
型としてのthis
thisを用いることでメソッドチェーンのような記述が可能。
thisを用いることで継承したクラスがあった場合はその継承したクラスのオブジェクトを返すことが可能
ジェネリック
下記のように配列に特定の型情報だけ限定する場合
let data: Array<number> = [1, 2, 3]
クラスで表現するケース
Tの部分が一般化されているため型を設定できる。
型引数は複数与えることも可能
class MyGenerics<T> {
value: T;
getValue(): T {
return this.value;
}
}
let g = new MyGenerics<string>();
g.value = 'Hoge';
console.log(g.getValue());
クラスの型付
class Hoge {}
class FooBar extends Hoge {}
class MyGenerics<T extends Hoge> {
value: T;
getValue(): T {
return this.value;
}
}
let g = new MyGenerics<FooBar>();
g.value = new FooBar();
console.log(g.getValue());
ジェネリックメソッド
class MyCollection {
static addAll<T>(data: T[],... values: T[]): T[] {
return data.concat(values);
}
}
let x = [10, 15, 30];
console.log(MyCollection.addAll(x, 35, 50));
オブジェクトをマージする関数
function merge<T, R>(obj1: T, obj2: R): T & R {
let result = <T & R> {};
for(let key in obj1) {
(<any>result)[key] = obj1[key];
}
for(let key in obj2) {
(<any>result)[key] = obj2[key];
}
return result;
}
class Book {
constructor(private title: string, private price: number) {}
toString() {
return this.title + ' ' + this.price;
}
}
class Logger {
debug() {
console.log(this.toString());
}
}
let m = merge(new Book('JavaScript Beginner', 2980), new Logger());
console.log(m);
VS Code
書籍では詳細にVS Codeを用いた実装方法が記述されていました。
最後に
WINGSプロジェクトの書籍レビュアーに応募し、献本して頂いたので、書評を書きました。
文法を理解するのに体系的にまとめられており、実際に使用できるツールの紹介もあったので良かったです。
JavaScriptで作成したものをTypeScriptにリプレースするのに最適だと思います。
本記事では文法の羅列になっていますが書籍では個々の内容の説明も詳しく記述されています。
WINGSプロジェクト
- http://www.wings.msn.to/