TypeScript入門と基礎と使い方

TypeScript入門と基礎と使い方

node.jsでTypeScriptの基礎を勉強します。

nodistはインストールされているものとします。インストール方法は「nodistをインストールする」を参照ください。

プロジェクトを作成する

まずプロジェクトを作成します。

npm init -y

次にtypescriptをグローバルインストールします。

npm install -g typescript

次にnode.jsの型定義ファイルをインストールします。良く分からない場合はおまじないとでも思ってください。型定義ファイルをインストールしないとtscのコンパイルが通りません。

※型定義ファイルはいっぱいあります。型定義ファイル検索サイト

npm i -S @types/node

「 -D」は「–save-dev」と同じです。「-S」は「–save」と同じです。

これで開発環境はとりあえず整いました。

TypeScriptはAltJSと呼ばれており、JSの次世代プログラミング言語といわれています。

AltJSは次世代プログラミング言語の総称であって、その代表格がTypeScriptです。

TypeScriptの拡張子

TypeScriptファイルの拡張子は「.ts」です。

a.tsファイルを作成してみます。

let button = document.createElement('button');
button.textContent = "ボタン";
button.onclick = function() {
  alert('yes');
}
document.body.appendChild(button);

このファイルをコンパイルしてみます。コンパイルにはtscコマンドを使用します。

tsc a.ts

これでa.jsが作成されていることが確認できます。

TypeScript入門と基礎と使い方

sourcemapも出力したい場合は

tsc  --sourcemap a.ts

とすると、a.js.mapが作成され、デバッグできるようになります。デバッグ方法については後述します。

a.js内をPlaygroundに貼り付けて実行してみます。

TypeScript入門と基礎と使い方

オンラインエディタで勉強する

typescriptを記述し、トランスパイルしてくれるオンラインエディタがあります。

Playgroundで色々触るとすぐに覚えることができると思います。

tsconfig.jsonファイルを作成する

プロジェクトのルートディレクトリにtsconfig.jsonファイルを作成します。tscコマンドでいちいちオプションをつけてコンパイルするケースは少ないはずですので、普通のプロジェクトなら必ず存在するファイルだと思います。

このファイルはTypeScriptプロジェクトのルートディレクトリであることを示すファイルでもあります。

このファイルでは、TypeScriptプロジェクトをコンパイルするのに必要なコンパイラのオプションを指定しておきます。

tsc --init

でデフォルトのtsconfig.jsonが作成されます。

以下、記述例です。

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es2017",
    "sourceMap": true,
    "types" : ["node"],
    "strict": true,
    "strictNullChecks" : true // nullやundefinedを許容しない
  },
  "exclude": [
    "node_modules"
  ]
}

参考サイト

nullやundefinedを許容すると、これもバグの温床になるので、許容しないように設定すべきです。tsconfig.jsonファイルで許容しないように設定することをお勧めします。

let a = null; // 型を指定しない場合、型推論されるのでコンパイルが通ってしまう

上記は実はコンパイル通りますので気をつけましょう。型推論については後述します。

let a:string = null;

stringというように変数に型を指定した場合にnullやundefinedを代入するとエラーとなります。

tslintの設定をする

tslint + prettier を設定」を参照ください。

npm scriptsでビルド設定する

いちいちtscコマンドをタイプしてられないので、npm scriptsを指定しましょう。

package.json(一部)

"scripts": {
  "build": "tsc",
  "watch": "tsc --watch"
}

これで

npm run build

で、TypeScriptをコンパイルすることが可能になります。

TypeScriptとJavaScriptの違い

この2つの違いはまず拡張子がjs,tsで違います。

それと決定的に違うのはJavaScriptが動的型付けであるのに対し、TypeScriptは静的型付けである、という点です。

JavaScriptの変数には数値や文字列や関数が入ったりします。変数の型が動的に変わるわけです。これはすごくプログラムバグを作りやすいと思います。静的型付けはJavaなどのように変数宣言時に変数の型を指定するため、変数の型はかわりませんので型間違いのバグがなくなるといった利点があります。

TypeScriptのプリミティブ型

内容
string 文字列
number 数値
boolean 真偽値
symbol シンボル

TypeScriptの型は非常に多く、Pythonでも使うタプル型などもあります。

プリミティブ型の変数の定義例です。

const a:string = 'S';
const b:number = 3.1; // 初期化ができる
const c:number; // const型で初期化しない場合はエラーとなるので、この場合はletを使用する

TypeScriptのその他の型

TypeScriptには非常に多くの型があります。

その一つがany型です。any型はなんでも入る型です。

const a:any = 3;
const b:string = a;

変数aはany型なので、変数bに代入することができます。

ただしTypeScriptの静的型付けという便利な特徴が台無しなので多用するのは良くない型だと思います。

既存のJSとの互換性を保つ為にanyを使うくらいだと思います。それでも共用型という型がTypeScriptにはあるので、any型より共用型を使用することをお勧めします。

ここで気になるのは、変数bの型はstringなのかnumberなのかanyなのか、です。

なんと変数bはnumberに変換されてしまいます。これはちょっと注意ですね。

Object型はJSではめっきり以下の記述するようになりました。

const obj = {a:1};

これをTypeScriptで記述すると以下のようになります。

const obj = {a:1};

同じですね。実はTypeScriptには{}型というのがあります。

これについても後日記載します。。

TypeScriptの型推論

TypeScriptでは型推論してくれます。これは便利です。

let a = 1;// numberと型推論する
a = 'str'; // エラーとなります

変数の初期化

変数を初期化すると型推論してくれます。

では初期化しないとどうなるかというとany型になります。

また、nullやundefinedで初期化した場合もany型となります。

ただし、設定ファイルで動作は変わります。

TypeScriptの型キャスト

Javaと同じくキャストすることができます。型アサーションともいうようです。

string型の文字列長をnumberで受け取ってみます。キャストするときは<string>というように記述します。

let s: any = 'abcde';
let n: number = (<string>s).length;
console.log(n); // 5

<number>という記述方法でキャストしますが、asキーワードで型キャストすることもできます。変数名 as numberというように記述します。上記を書き換えてみます。

let s: any = 'abcde';
let n: number = (s as string).length;
console.log(n); // 5

TypeScriptの共用型

any型を使うなら共用型という型があります。共用型とは複数の戻り値を許容する方です。

型を|で区切って指定します。以下記述例です。

let a:string | number;

stringかnumberを許容しますが、booleanが代入されるとエラーとなります。関数の型でも使えたりするので共用型は積極的に使いましょう。

TypeScriptのenum型

TypeScriptではenum型が使えます。これは以下のように記述します。

enum AAA {
  a,
  b,
  c
}
let ret:AAA=AAA.a;
console.log(ret);// 0と表示される

enumの列挙子はデフォルトで0,1,2…と初期化されます。

もちろん初期化することも可能です。

enum AAA {
  a = 5,
  b = 3,
  c = 1
}
let r:AAA=AAA.a;
console.log(r);
let s:AAA=AAA.b;
console.log(s);
let t:AAA=AAA.c;
console.log(t);

結果は以下のようになります。

5
3
1

途中で初期化することも可能です。

enum AAA {
  a, // 0
  b, // 1
  c = 10, // 10
  d // 11
}

配列の宣言

TypeScriptで配列の宣言方法です。

let arr1:number[] = [1,2,3];// 1次元配列の宣言
let arr2:string[][] = [['a','b'],['c','d']]; // 2次元配列の宣言

連想配列(ハッシュ)の宣言

TypeScriptで連想配列(ハッシュ)を宣言してみます。

let hash:{[key:number]:string;} = {
  0:'a',
  1:'b'
}

変数名hashが連想配列です。keyは実はiでも何でもいいです。連想配列のキーの型を指定する為に必要なだけで、今回はnumber型をキーとしています。

:string;でキーに対する値の型を指定しています。ここではstring型を指定しています。

試しにエラーにしてみたいと思います。

let hash:{[key:number]:string;} = {
  'a':'a',
  1:'b'
}

これでnpm run buildとすると、「error TS2322: Type ‘{ ‘a’: string; 1: string; }’ is not assignable to type ‘{ [key: number]: string; }’.」とエラーが確認できます。

連想配列のインデックスシグネチャは必須

連想配列のインデックスシグネチャとは、{[key:number]:string;}の部分です。

連想配列のインデックスシグネチャを省略してみます。きっと型推論してくれるので、、。

let hash = {
  'a':'a',
  'b':'b'
}

hash['c'] = 'c'; // ここでエラーとなる

インデックスシグネチャを省略すると、暗黙的にany型として型推論されてしまうため、エラーとなります。実質、連想配列のインデックスシグネチャは必須です。

interfaceキーワードの使い方

interfaceはJavaと同じくclassにimplementsしたりすることができますが、他にも色々使い方があります。

まずクラスにimplementsします。

interface SampleI {
  name:string; // privateやpublicはつけられない
}

class Sample implements SampleI {
  private name:string='test'; // 自動的にpublicとなる
  public getName(){
    return this.name;
  }
}
let sample = new Sample();
console.log(sample.getName()); // testと表示される

ちょっと気を付けないといけないのが、interfaceで定義したプロパティは実装するクラスではpublicとなります。明示的にprivateをつけるとエラーとなりますので注意です。

interfaceキーワードを使用して連想配列のインデックスシグネチャを定義する

TypeScriptでは、interfaceキーワードを使用して連想配列で実質必須であるインデックスシグネチャを定義することができます。

以下、定義例です。

interface SampleSignature {
  [i:string]:string;
}

let hash:SampleSignature = {
  'a':'a',
  'b':'b'
}

さきほども書きましたがiでもindexでもkeyでもなんでもいいです。キーの型名を指定するために必要となります。

interfaceの代わりにtypeofを使用する

インデックスシグネチャをinterfaceで定義するのは、そのインデックスシグネチャをよく使う場合のみだと思います。局所的にしか使わないインデックスシグネチャはべた書きする、もしくはtypeofを使用すればよいと思います。

let sigI: {top?: number; left?: number}; // letでないとエラーとなるので注意
let aaa: typeof sigI = {top:10,left:20};
console.log(aaa.top); // 10と表示される

TypeScriptのタプル型

TypeScriptのタプル型は配列とよく似ています。各要素の型が異なる点が特徴です。

let touple:[string,number] = ['abc',123];
console.log(typeof touple[0]); // string
console.log(typeof touple[1]); // number

TypeScriptのtry-catch

TypeScriptではtry-catch句が使えます。finally句も使えるようです。

try句で意図的にthrowしてみると、catch句に遷移します。

try {
  throw Error('aaaa');
} catch(e) {
  console.log(e.name); // Error
  console.log(e.message); // aaaa
  console.log(e.stack);  // スタックトレースが表示される
}

TypeScriptの関数宣言

TypeScriptの関数の記述方法です。

const aa = function(a:number, b:number):string{
  return (a + b).toString();
}

console.log(aa(3,5));
console.log(typeof aa);

number型のa,number型のbという引数と、戻り値はstringというように定義しています。

実行結果は以下のようになります。

8
function

このことからTypeScriptにはfunction型という型が存在することがわかります。

関数宣言をアロー関数で宣言する

やっぱりアロー関数でせんげんしてみましょう。thisを束縛してくれるのでアロー関数が断然便利ですから。

TypeScriptのアロー関数宣言は簡単です。

const a = function (a:number, b:number):string{ return 'a'; }; // 普通の関数宣言
const b = (a:number,b:number):string => { return 'a'; };// アロー関数宣言
const c = (a:number,b:number):string => 'a'; // これもreturnを省略したアロー関数

TypeScriptのアロー関数は、本体が1文の場合は{}とreturnを省略することができます。

関数の引数を任意引数にする

TypeScriptでは引数を任意にすることができ、引数がなければ省略することができます。

const b = (a?:string):string => {
  if (a !== undefined) {
    return a;
  }
  return 'error';
};

console.log(b()); // 引数なしでもエラーとならず、errorと出力される

undefinedのチェックをしているのは、引数aは任意引数のため、型推論でstring | undefined型となってしまうからです。

関数の引数が省略された場合デフォルト値を設定する

TypeScriptでも引数にデフォルト値を指定することができます。

引数が省略された場合は仮引数にデフォルト値が設定されるため、undefinedのチェックが不要になりますのでお勧めします。

const b = (a:string = 'aiueo'):string => { // = 初期値 というように記述する
  return a;
};

console.log(b()); // aiueoと出力される
console.log('aaa'); // aaaと出力される

TypeScriptのclass宣言

TypeScriptのクラス宣言では、プロパティにはlet,constなどはつけません。extendsキーワードを使用して、継承することも可能です。

class Parent {
  name: string;
}
classs Emp extends Parent {
  id: number;
}

classの配列を宣言、初期化がすることができます。

class Parent {
  name: string;
}
let arr: Parent[];
arr = [
  { name: 'Takahashi' },
  { name: 'Maruyama' }
];

非同期処理にasync/awaitが使える

TypeScriptではPromiseも使えますが、async/awaitが使えます。こっちのほうが簡単でPromise地獄に陥ることがないのでお勧めです。async/awaitについては「node v8以降の非同期処理はasync/awaitを使用しよう」を参照ください。

TypeScriptのポイント

  • varは使わず、基本はconst,無理ならletを使用する
  • anyはなるべく使わない、代わりに共用型を使用する
  • nullとundefinedはtsconfig.jsonで許容しない設定にする
  • let,const,class,extends,アロー関数,デストラクチャリングなどが使える
  • 静的型付けである

コメント

タイトルとURLをコピーしました